]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/View/Base.pm
fix classmetadata
[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} = $r->{template_args};
60     delete $r->{template_args}{classmetadata}; # already overrides
61     %args = ( %args, %{ $r->{template_args} || {} } );
62     %args;
63 }
64
65 sub process {
66     my ( $self, $r ) = @_;
67     $r->{content_type}      ||= "text/html";
68     $r->{document_encoding} ||= "utf-8";
69     my $status = $self->template($r);
70     return $self->error($r) if $status != OK;
71     return OK;
72 }
73
74 sub error {
75     my ( $self, $r, $desc ) = @_;
76     $desc = $desc ? "$desc: " : "";
77     warn $desc . $r->{error} ."\n";
78     if ( $r->{error} =~ /not found$/ ) {
79
80         # This is a rough test to see whether or not we're a template or
81         # a static page
82         return -1 unless @{ $r->{objects} || [] };
83
84         $r->{error} = <<EOF;
85
86 <H1> Template not found </H1>
87
88 This template was not found while processing the following request:
89
90 <B>@{[$r->{action}]}</B> on table <B>@{[ $r->{table} ]}</B> with objects:
91
92 <PRE>
93 @{[join "\n", @{$r->{objects}}]}
94 </PRE>
95
96 Looking for template <B>@{[$r->{template}]}</B> in paths:
97
98 <PRE>
99 @{[ join "\n", $self->paths($r) ]}
100 </PRE>
101 EOF
102         $r->{content_type} = "text/html";
103         $r->{output}       = $r->{error};
104         return OK;
105     }
106     $r->{content_type} = "text/plain";
107     $r->{output}       = $r->{error};
108     $r->send_output;
109     return ERROR;
110 }
111
112 sub template { die shift() . " didn't define a decent template method!" }
113
114 1;
115
116
117 =head1 NAME
118
119 Maypole::View::Base - Base cl
120
121 =head1 DESCRIPTION
122
123 This is the base class for Maypole view classes. This is an abstract class
124 meant to define the interface, and can't be used directly.
125
126 =head2 process
127
128 This is the engine of this module. It populates all the relevant variables
129 and calls the requested action.
130
131 Anyone subclassing this for a different database abstraction mechanism
132 needs to provide the following methods:
133
134 =head2 template 
135
136 In this method you do the actual processing of your template. it should use L<paths> 
137 to search for components, and provide the templates with easy access to the contents
138 of L<vars>. It should put the result in $r->{output} and return OK if processing was
139 sucessfull, or populate $r->{error} and return ERROR if it fails.
140
141 =head1 Other overrides
142
143 Additionally, individual derived model classes may want to override the
144
145 =head2 new
146
147 The default constructor does nothing. You can override this to perform actions
148 during view initialization.
149
150 =head2 paths
151
152 Returns search paths for templates. the default method returns factory, custom and
153 <tablename> under the configured template root.
154
155 =head2 vars
156
157 returns a hash of data the template should have access to. The default one populates
158 classmetadata if there is a class, as well as setting the data objects by name if 
159 there is one or more objects available.
160
161 =head2 error
162
163
164 =cut