| 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
|
|
|
|
|
|
|
|