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