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