]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/View/Base.pm
+ Use HTTP::Headers for input/output headers. Add appropriate unit tests.
[maypole.git] / lib / Maypole / View / Base.pm
1 package Maypole::View::Base;
2 use File::Spec;
3 use UNIVERSAL::moniker;
4 use strict;
5 use Maypole::Constants;
6
7 sub new { bless {}, shift }    # By default, do nothing.
8
9 sub paths {
10     my ( $self, $r ) = @_;
11     my $root = $r->config->template_root || $r->get_template_root;
12     return (
13         $root,
14         (
15             $r->model_class
16               && File::Spec->catdir( $root, $r->model_class->moniker )
17         ),
18         File::Spec->catdir( $root, "custom" ),
19         File::Spec->catdir( $root, "factory" )
20     );
21 }
22
23 sub vars {
24     my ( $self, $r ) = @_;
25     my $class = $r->model_class;
26     my $base  = $r->config->uri_base;
27     $base =~ s/\/+$//;
28     my %args = (
29         request => $r,
30         objects => $r->objects,
31         base    => $base,
32         config  => $r->config
33
34           # ...
35     );
36     if ($class) {
37         my $classmeta = $args{classmetadata} ||= {};
38         $classmeta->{name}              ||= $class;
39         $classmeta->{description}       ||= $class->description;
40         $classmeta->{table}             ||= $class->table;
41         $classmeta->{columns}           ||= [ $class->display_columns ];
42         $classmeta->{list_columns}      ||= [ $class->list_columns ];
43         $classmeta->{colnames}          ||= { $class->column_names };
44         $classmeta->{related_accessors} ||= [ $class->related($r) ];
45         $classmeta->{moniker}           ||= $class->moniker;
46         $classmeta->{plural}            ||= $class->plural_moniker;
47         $classmeta->{cgi}               ||= { $class->to_cgi };
48
49         # User-friendliness facility for custom template writers.
50         if ( @{ $r->objects || [] } > 1 ) {
51             $args{ $r->model_class->plural_moniker } = $r->objects;
52         }
53         else {
54             ( $args{ $r->model_class->moniker } ) = @{ $r->objects || [] };
55         }
56     }
57
58     # Overrides
59     local $r->{template_args}{classmetadata}; # already overrides
60     %args = ( %args, %{ $r->{template_args} || {} } );
61     %args;
62 }
63
64 sub process {
65     my ( $self, $r ) = @_;
66     $r->{content_type}      ||= "text/html";
67     $r->{document_encoding} ||= "utf-8";
68     my $status = $self->template($r);
69     return $self->error($r) if $status != OK;
70     return OK;
71 }
72
73 sub error {
74     my ( $self, $r, $desc ) = @_;
75     $desc = $desc ? "$desc: " : "";
76     warn $desc . $r->{error} ."\n";
77     if ( $r->{error} =~ /not found$/ ) {
78
79         # This is a rough test to see whether or not we're a template or
80         # a static page
81         return -1 unless @{ $r->{objects} || [] };
82
83         $r->{error} = <<EOF;
84
85 <H1> Template not found </H1>
86
87 This template was not found while processing the following request:
88
89 <B>@{[$r->{action}]}</B> on table <B>@{[ $r->{table} ]}</B> with objects:
90
91 <PRE>
92 @{[join "\n", @{$r->{objects}}]}
93 </PRE>
94
95 Looking for template <B>@{[$r->{template}]}</B> in paths:
96
97 <PRE>
98 @{[ join "\n", $self->paths($r) ]}
99 </PRE>
100 EOF
101         $r->{content_type} = "text/html";
102         $r->{output}       = $r->{error};
103         return OK;
104     }
105     $r->{content_type} = "text/plain";
106     $r->{output}       = $r->{error};
107     $r->send_output;
108     return ERROR;
109 }
110
111 sub template { die shift() . " didn't define a decent template method!" }
112
113 1;
114
115
116 =head1 NAME
117
118 Maypole::View::Base - Base cl
119
120 =head1 DESCRIPTION
121
122 This is the base class for Maypole view classes. This is an abstract class
123 meant to define the interface, and can't be used directly.
124
125 =head2 process
126
127 This is the engine of this module. It populates all the relevant variables
128 and calls the requested action.
129
130 Anyone subclassing this for a different database abstraction mechanism
131 needs to provide the following methods:
132
133 =head2 template 
134
135 In this method you do the actual processing of your template. it should use L<paths> 
136 to search for components, and provide the templates with easy access to the contents
137 of L<vars>. It should put the result in $r->{output} and return OK if processing was
138 sucessfull, or populate $r->{error} and return ERROR if it fails.
139
140 =head1 Other overrides
141
142 Additionally, individual derived model classes may want to override the
143
144 =head2 new
145
146 The default constructor does nothing. You can override this to perform actions
147 during view initialization.
148
149 =head2 paths
150
151 Returns search paths for templates. the default method returns factory, custom and
152 <tablename> under the configured template root.
153
154 =head2 vars
155
156 returns a hash of data the template should have access to. The default one populates
157 classmetadata if there is a class, as well as setting the data objects by name if 
158 there is one or more objects available.
159
160 =head2 error
161
162
163 =cut