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