line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::View::Seamstress; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
22781
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
39
|
|
4
|
|
|
|
|
|
|
# [10:05:57] <@andyg> Catalyst::View is the correct base class |
5
|
1
|
|
|
1
|
|
5
|
use base qw/Catalyst::View/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
580
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
2721
|
use Data::Dumper; |
|
1
|
|
|
|
|
18859
|
|
|
1
|
|
|
|
|
675
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '2.2'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Catalyst::View::Seamstress - HTML::Seamstress View Class for Catalyst |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# use the helper to create MyApp::View::Seamstress |
19
|
|
|
|
|
|
|
# where comp_root and skeleton are optional |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
myapp_create.pl view Seamstress Seamstress /path/to/html html::skeleton |
22
|
|
|
|
|
|
|
^-modulenm ^-helpernm ^-comp_root ^-skeleton |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# optionally edit the skeleton and meat_pack routines |
25
|
|
|
|
|
|
|
# in lib/MyApp/View/Seamstress.pm |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# create your seamstress template packaged with spkg.pl |
28
|
|
|
|
|
|
|
# see HTML::Seamstress.. This will give you a .pm file to go with your html, |
29
|
|
|
|
|
|
|
# so something like html::helloworld |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# render view from lib/MyApp.pm or lib/MyApp::C::SomeController.pm |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub message : Global { |
34
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# LOOM points to our template class made with spkg.pl or |
37
|
|
|
|
|
|
|
# manually: |
38
|
|
|
|
|
|
|
$c->stash->{LOOM} = 'html::hello_world'; |
39
|
|
|
|
|
|
|
$c->stash->{name} = 'Mister GreenJeans'; |
40
|
|
|
|
|
|
|
$c->stash->{date} = 'Today'; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# the DefaultEnd plugin would mean no need for this line |
43
|
|
|
|
|
|
|
$c->forward('MyApp::View::Seamstress'); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# and in your html::helloworld you can do something like: |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub process{ |
49
|
|
|
|
|
|
|
my( $tree, $c, $stash ) = @_; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
$tree->look_down( id => 'name' )->replace_content( $stash->{name} ); |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head1 DESCRIPTION |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
This is the Catalyst view class for L<HTML::Seamstress|HTML::Seamstress>. It allows |
59
|
|
|
|
|
|
|
templating with proper seperation between code and HTML. This means you can get a |
60
|
|
|
|
|
|
|
designer/friend/client/stooge to make your templates for you without having to |
61
|
|
|
|
|
|
|
teach them a mini-language! |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
Your application should define a view class which is a subclass of |
64
|
|
|
|
|
|
|
this module. The easiest way to achieve this is using the |
65
|
|
|
|
|
|
|
F<myapp_create.pl> script (where F<myapp> should be replaced with |
66
|
|
|
|
|
|
|
whatever your application is called). This script is created as part |
67
|
|
|
|
|
|
|
of the Catalyst setup. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
$ script/myapp_create.pl view Seamstress Seamstress |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
This creates a MyApp::View::Seamstress.pm module in the |
72
|
|
|
|
|
|
|
F<lib> directory (again, replacing C<MyApp> with the name of your |
73
|
|
|
|
|
|
|
application). |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Now you can modify your action handlers in the main application and/or |
77
|
|
|
|
|
|
|
controllers to forward to your view class. You might choose to do this |
78
|
|
|
|
|
|
|
in the end() method, for example, to automatically forward all actions |
79
|
|
|
|
|
|
|
to the Seamstress view class. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# In MyApp or MyApp::Controller::SomeController |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub end : Private { |
84
|
|
|
|
|
|
|
my( $self, $c ) = @_; |
85
|
|
|
|
|
|
|
$c->forward('MyApp::View::Seamstress'); |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Or you might like to use |
89
|
|
|
|
|
|
|
L<Catalyst::Plugin::DefaultEnd|Catalyst::Plugin::DefaultEnd> |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
..or even |
92
|
|
|
|
|
|
|
L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head1 CONFIGURATION |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
The helper app automatically puts the per-application |
98
|
|
|
|
|
|
|
configuration info in C<MyApp::View::Seamstress>. You configure the |
99
|
|
|
|
|
|
|
per-request information (e.g. C<< $c->stash->{LOOM} >> and |
100
|
|
|
|
|
|
|
variables for this template) in your controller. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
The two main options which control how View::Seamtress renders HTML are the |
103
|
|
|
|
|
|
|
LOOM (which is taken from the stash) and optionally the skeleton, which is |
104
|
|
|
|
|
|
|
stored in the app config. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
If you just configure a LOOM then you are most likely using the "plain meat" method described below. If you also configure a skeleton in your config as well then you're using the "meat and skeleton" method. See below for a more detailed discussion of this! |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=over |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=item * C<< $c->stash->{LOOM} >> |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
The Seamstress view plugin MUST have a LOOM |
113
|
|
|
|
|
|
|
to work on or it |
114
|
|
|
|
|
|
|
will balk with an error: |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub message : Global { |
117
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
118
|
|
|
|
|
|
|
$c->stash->{LOOM} = 'html::hello_world'; |
119
|
|
|
|
|
|
|
$c->stash->{name} = 'Billy Bob'; |
120
|
|
|
|
|
|
|
$c->stash->{date} = 'medjool sahara'; |
121
|
|
|
|
|
|
|
$c->forward('MyApp::View::Seamstress'); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=item * C<< MyApp::View::Seamstress->config->{skeleton} >> |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
By default this is not set and the HTML output is simply the result of |
128
|
|
|
|
|
|
|
taking C<< $c->stash->{LOOM} >>, calling C<new()> to create |
129
|
|
|
|
|
|
|
an HTML tree and then passing this to C<process()> so that it can rework |
130
|
|
|
|
|
|
|
the tree. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
However, if C<< MyApp::View::Seamstress->config->{skeleton} >> is |
133
|
|
|
|
|
|
|
set, then both its value and the values of |
134
|
|
|
|
|
|
|
C<< MyApp::View::Seamstress->config->{meat_pack} >> |
135
|
|
|
|
|
|
|
and C<< $stash->{LOOM}->fixup() >> |
136
|
|
|
|
|
|
|
come into effect |
137
|
|
|
|
|
|
|
as described in L<HTML::Seamstress/"The_meat-skeleton_paradigm">. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Let's take that a little slower: C<< $stash->{LOOM}->fixup() >> |
140
|
|
|
|
|
|
|
means: given a Seamstress-style Perl class, whose name is |
141
|
|
|
|
|
|
|
C<< $stash->{LOOM} >>, call the method C<fixup()> in that |
142
|
|
|
|
|
|
|
class so that it can do a final fixup of the entire HTML that is about |
143
|
|
|
|
|
|
|
to be shipped back to the client. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=back |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
The output generated from the LOOM |
148
|
|
|
|
|
|
|
(and possibly its interaction with a skeleton) |
149
|
|
|
|
|
|
|
is stored in |
150
|
|
|
|
|
|
|
C<< $c->response->body >>. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head2 Other Config Options |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=over |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=item config->{fixup} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Set this to a coderef to allow the view to change the tree after the main |
160
|
|
|
|
|
|
|
processing phase. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item config->{use_xhtml} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
By default the view will generate html 4 style html by calling as_HTML on the |
165
|
|
|
|
|
|
|
tree object. If you set this to a true value it will generate XHTML style HTML |
166
|
|
|
|
|
|
|
by calling as_XML on the tree object. See L<HTML::Element> for details for |
167
|
|
|
|
|
|
|
these methods. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Also note that this won't apply proper HTML doctypes and what-have-you unless |
170
|
|
|
|
|
|
|
you have them in your original HTML. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=item config->{meat_pack} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
This is the subref which is called to pack meat into the skeleton for the meat |
175
|
|
|
|
|
|
|
skeleton method. Tinker with this to have more creative LOOMS. See "Funny |
176
|
|
|
|
|
|
|
LOOMs" and the meat/skeleton discussions. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=back |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 Funny LOOMs |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
In the examples so far the LOOM has always been a class name. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
If instead LOOM is an object then we'll assume that is a useful HTML::Element style |
185
|
|
|
|
|
|
|
object and just use that instead of calling C<new> on the LOOM. In this case we'll also not ->delete it at the end of the request so you'll have to do that yourself! |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
If the LOOM is in fact an ARRAY reference filled with class names we'll send the meat_pack a hash of class names mapped to objects. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=cut |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# process() |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# C<< eval-requires >> the module specified in C<< $c->stash->{LOOM} >>. |
195
|
|
|
|
|
|
|
# Gets the |
196
|
|
|
|
|
|
|
# C<HTML::Tree> representation of the file via C<new> and then calls |
197
|
|
|
|
|
|
|
# C<< $self->process($c, $c->stash) >> to rewrite the tree. |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub page2tree { |
200
|
0
|
|
|
0
|
0
|
|
my ($self, $c, $page_class, $process_method) = @_; |
201
|
|
|
|
|
|
|
|
202
|
0
|
0
|
|
|
|
|
$c->log->debug(qq/Rendering template "$page_class"/) if $c->debug; |
203
|
|
|
|
|
|
|
|
204
|
0
|
|
0
|
|
|
|
$process_method ||= 'process'; |
205
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
my $page_object; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# IF we've been passed a page class, build an object: |
209
|
0
|
0
|
|
|
|
|
if (not ref $page_class) { |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# pull in the page class: |
212
|
0
|
|
|
|
|
|
eval "require $page_class"; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# emit errors if there were problems with the page_class: |
215
|
0
|
0
|
|
|
|
|
if ($@) { |
216
|
0
|
|
|
|
|
|
my $error = qq/Couldn't load $page_class -- "$@"/; |
217
|
0
|
|
|
|
|
|
$c->log->error($error); |
218
|
0
|
|
|
|
|
|
$c->error($error); |
219
|
0
|
|
|
|
|
|
return 0; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
0
|
|
|
|
|
|
$page_object = $page_class->new($c); # e.g html::hello_world->new |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
# IF we've been passed a page object, just use it: |
225
|
|
|
|
|
|
|
else { |
226
|
0
|
|
|
|
|
|
$page_object = $page_class; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# Run the process hook: |
230
|
0
|
|
|
|
|
|
my $tree; eval { $tree = $page_object->$process_method($c, $c->stash) } ; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
|
232
|
0
|
0
|
|
|
|
|
if ( my $error = $@ ) { |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
chomp $error; |
235
|
0
|
|
|
|
|
|
$error = qq/process() failed in "$page_class". Error: "$error"/; |
236
|
0
|
|
|
|
|
|
$c->log->error($error); |
237
|
0
|
|
|
|
|
|
$c->error($error); |
238
|
0
|
|
|
|
|
|
return undef; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
} else { |
241
|
|
|
|
|
|
|
|
242
|
0
|
|
|
|
|
|
return $tree; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Main view process hook: |
249
|
|
|
|
|
|
|
sub process { |
250
|
0
|
|
|
0
|
0
|
|
my ( $self, $c ) = @_; |
251
|
|
|
|
|
|
|
|
252
|
0
|
|
|
|
|
|
my $body_is_skeleton = 0; |
253
|
|
|
|
|
|
|
|
254
|
0
|
|
|
|
|
|
my ($skeleton, $meat, $body) ; |
255
|
|
|
|
|
|
|
|
256
|
0
|
|
|
|
|
|
my $loom = $c->stash->{LOOM}; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# check we actually got a loom to work with: |
259
|
0
|
0
|
|
|
|
|
unless ($loom) { |
260
|
0
|
0
|
|
|
|
|
$c->log->debug('No LOOM specified for rendering') if $c->debug; |
261
|
0
|
|
|
|
|
|
return 0; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
0
|
0
|
|
|
|
|
unless ( $c->response->content_type ) { |
265
|
0
|
|
|
|
|
|
$c->response->content_type('text/html; charset=utf-8'); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
|
269
|
0
|
0
|
|
|
|
|
if (ref($loom) eq 'ARRAY') { |
270
|
0
|
|
|
|
|
|
map { |
271
|
0
|
|
|
|
|
|
$meat->{$_} = $self->page2tree($c, $_); |
272
|
|
|
|
|
|
|
} @$loom; |
273
|
|
|
|
|
|
|
} else { |
274
|
0
|
|
|
|
|
|
$meat = $body = $self->page2tree($c, $loom); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# |
278
|
|
|
|
|
|
|
# render and pack MyApp::View::Seamstress->config->{skeleton} |
279
|
|
|
|
|
|
|
# if defined |
280
|
|
|
|
|
|
|
# |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
|
283
|
0
|
0
|
|
|
|
|
if ($skeleton = $self->config->{skeleton}) { |
284
|
0
|
|
|
|
|
|
$skeleton = $self->page2tree($c, $skeleton); |
285
|
|
|
|
|
|
|
|
286
|
0
|
|
|
|
|
|
$self->config->{meat_pack}->( |
287
|
|
|
|
|
|
|
$self, $c, $c->stash, $meat, $skeleton |
288
|
|
|
|
|
|
|
); |
289
|
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
$body_is_skeleton = 1; |
291
|
0
|
|
|
|
|
|
$body = $skeleton ; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# give the main view config an opportunity to twiddle the tree a bit: |
295
|
0
|
0
|
|
|
|
|
if ( ref $self->config->{fixup} ) { |
296
|
0
|
|
|
|
|
|
$self->config->{fixup}->($body, $c); |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# take the the body and make some REAL html out of it! |
301
|
0
|
|
|
|
|
|
my $response_body; |
302
|
0
|
0
|
|
|
|
|
if ( $c->config->{use_xhtml} ) { |
303
|
0
|
|
|
|
|
|
$response_body = $body->as_XML( undef, ' ' ); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
else { |
306
|
0
|
|
|
|
|
|
$response_body = $body->as_HTML(undef, ' ') |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# stuff the response_body in the response body! |
310
|
0
|
|
|
|
|
|
$c->response->body( $response_body ); |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# we delete the body unless our loom ( or skeleton if we have one) is a reference |
314
|
|
|
|
|
|
|
# which we take as a sign that the user is doing something more elaborate caching or something.. |
315
|
0
|
0
|
0
|
|
|
|
unless( (! $body_is_skeleton && ref $loom) || ( $body_is_skeleton && ref $self->config->{skeleton} ) ) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
316
|
0
|
|
|
|
|
|
$body->delete; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
|
return 1; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
;1; |
323
|
|
|
|
|
|
|
__END__ |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=head1 The meat-skeleton paradigm |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Generally Catalyst::View::Seamstress operates in one of 2 ways: a plain meat |
328
|
|
|
|
|
|
|
way or a meat-skeleton way. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
Plain meat is simple: the View takes C<$c->stash->{LOOM} > and calls |
331
|
|
|
|
|
|
|
C<new()> and C<process()> on it and stores the result in C<$c->response->body>. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Meat-skeleton is designed to facilitate the way that most web sites are |
334
|
|
|
|
|
|
|
typically designed: |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
HTML pages typically have meat and a skeleton. The meat varies from page |
337
|
|
|
|
|
|
|
to page while the skeleton is fairly (though not completely) |
338
|
|
|
|
|
|
|
static. For example, the skeleton of a webpage is usually a header, a |
339
|
|
|
|
|
|
|
footer, and a navbar. The meat is what shows up when you click on a |
340
|
|
|
|
|
|
|
link on the page somewhere. While the meat will change with each |
341
|
|
|
|
|
|
|
click, the skeleton is rather static. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Mason accomodates the meat-skeleton paradigm via |
345
|
|
|
|
|
|
|
an C<autohandler> and C<< $m->call_next() >>. Template |
346
|
|
|
|
|
|
|
accomodates it via its C<WRAPPER> directive. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
And Seamstress? Well, here's what you _can_ do: |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=over |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=item 1 generate the meat, C<$meat> |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
This is typically what you see in the C<body> part of an HTML page |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=item 2 generate the skeleton, C<$skeleton> |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
This is typically the html, head, and maybe some body |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item 3 put the meat in the skeleton |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=back |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
So, nothing about this is forced. This is just how I typically do |
365
|
|
|
|
|
|
|
things and that is why |
366
|
|
|
|
|
|
|
L<Catalyst::View::Seamstress|Catalyst::View::Seamstress> has support |
367
|
|
|
|
|
|
|
for this. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head1 Tips to View Writers |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head2 The order of use base is VERY significant |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
When your helper module creates C<MyApp::View::Seamstress> it is B<very> |
374
|
|
|
|
|
|
|
important for the C<use base> to look this way: |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
use base qw(Catalyst::View::Seamstress HTML::Seamstress ); |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
and not this way: |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
use base qw(HTML::Seamstress Catalyst::View::Seamstress ); |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
so that certain calls (probably new) get handled properly. |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head2 Getting config information from MyApp and MyApp::View::* |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
assuming C<Catalyst::View::Seamstress::new()> starts off |
387
|
|
|
|
|
|
|
like this: |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
sub new { |
390
|
|
|
|
|
|
|
my $self = shift; |
391
|
|
|
|
|
|
|
my $c = shift; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
C<< $self->config >> contains things set in C<MyApp::View::*>. |
394
|
|
|
|
|
|
|
C<< $c->config >> contains things set in C<MyApp> |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
assuming C<Catalyst::View::Seamstress::process()> starts off |
397
|
|
|
|
|
|
|
similarly: |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub process { |
400
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
C<< $self->config >> contains things set in C<MyApp::View::*>. |
403
|
|
|
|
|
|
|
C<< $c->config >> contains things set in C<MyApp>. |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
There is no automatic merging of the two sources of configuration: you |
406
|
|
|
|
|
|
|
have to do that yourself if you want to do it. |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=head2 |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=head1 SEE ALSO |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
L<Catalyst>, |
415
|
|
|
|
|
|
|
L<Catalyst::View>, |
416
|
|
|
|
|
|
|
L<Catalyst::Helper::View::Seamstress>, |
417
|
|
|
|
|
|
|
L<HTML::Seamstress> |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head2 A working sample app |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
The best way to see a fully working Seamstress-style Perl class is to |
422
|
|
|
|
|
|
|
pull down the working sample app from sourceforge. |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
A working sample app, which does both simple and |
425
|
|
|
|
|
|
|
meat-skeleton rendering is available from github: |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
git clone git://github.com/draxil/catalyst--view--seamstress-sample-app.git |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head1 SUPPORT |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Email the author or ping him on C<#catalyst> on C<irc.perl.org> |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head1 AUTHORS |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Terrence Brannon <metaperl@gmail.com> |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
With some additional hacking by: |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
Joe Higton <draxil@cpan.org> |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head1 COPYRIGHT |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
This program is free software, you can redistribute it and/or modify it |
444
|
|
|
|
|
|
|
under the same terms as Perl itself. |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=cut |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
|