line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTML::Seamstress; |
2
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
385696
|
use strict; |
|
5
|
|
|
|
|
18
|
|
|
5
|
|
|
|
|
204
|
|
4
|
5
|
|
|
5
|
|
28
|
use warnings; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
179
|
|
5
|
|
|
|
|
|
|
|
6
|
5
|
|
|
5
|
|
36
|
use Carp qw(confess); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
313
|
|
7
|
5
|
|
|
5
|
|
26
|
use Cwd; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
327
|
|
8
|
5
|
|
|
5
|
|
5365
|
use Data::Dumper; |
|
5
|
|
|
|
|
56358
|
|
|
5
|
|
|
|
|
401
|
|
9
|
5
|
|
|
5
|
|
1961
|
use File::Slurp; |
|
5
|
|
|
|
|
31762
|
|
|
5
|
|
|
|
|
442
|
|
10
|
5
|
|
|
5
|
|
39
|
use File::Spec; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
144
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
5
|
|
|
5
|
|
137634
|
use HTML::Element::Library; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use HTML::Element::Replacer; |
16
|
|
|
|
|
|
|
use base qw/HTML::TreeBuilder HTML::Element/; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our $VERSION = '6.0' ; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub bless_tree { |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my ($node, $class) = @_; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
if (ref $node) { |
28
|
|
|
|
|
|
|
# warn "root node($class): ", $node->as_HTML; |
29
|
|
|
|
|
|
|
bless $node, $class ; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
foreach my $c ($node->content_list) { |
32
|
|
|
|
|
|
|
bless_tree($c, $class); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub new_from_file { # or from a FH |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
my ($class, $file) = @_; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
$class = ref $class ? ref $class : $class ; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $new = HTML::TreeBuilder->new_from_file($file); |
46
|
|
|
|
|
|
|
bless_tree($new, $class); |
47
|
|
|
|
|
|
|
#warn "CLASS: $class TREE:", $new; |
48
|
|
|
|
|
|
|
# warn "here is new: $new ", $new->as_HTML; |
49
|
|
|
|
|
|
|
$new; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub new_file { # or from a FH |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my ($class, $file, %args) = @_; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
-e $file or die 'File $file does not exist'; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
my $new = HTML::TreeBuilder->new; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
for my $k (keys %args) { |
62
|
|
|
|
|
|
|
next if $k =~ /guts/ ; # scales for more actions later |
63
|
|
|
|
|
|
|
$new->$k($args{$k}); |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
-e $file or die "$file does not exist"; |
67
|
|
|
|
|
|
|
$new->parse_file($file); |
68
|
|
|
|
|
|
|
bless_tree($new, $class); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
if ($args{guts}) { |
71
|
|
|
|
|
|
|
$new->guts; |
72
|
|
|
|
|
|
|
} else { |
73
|
|
|
|
|
|
|
$new; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub html { |
79
|
|
|
|
|
|
|
my ($class, $file, $extension) = @_; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
$extension ||= 'html'; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $pm = File::Spec->rel2abs($file); |
84
|
|
|
|
|
|
|
$pm =~ s!pm$!$extension!; |
85
|
|
|
|
|
|
|
$pm; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub eval_require { |
90
|
|
|
|
|
|
|
my $module = shift; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
return unless $module; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
eval "require $module"; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
confess $@ if $@; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub HTML::Element::xepand_replace { |
100
|
|
|
|
|
|
|
my $node = shift; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my $seamstress_module = ($node->content_list)[0] ; |
103
|
|
|
|
|
|
|
eval "require $seamstress_module"; |
104
|
|
|
|
|
|
|
die $@ if $@; |
105
|
|
|
|
|
|
|
$node->replace_content($seamstress_module->new) ; |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
1; |
111
|
|
|
|
|
|
|
__END__ |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head1 NAME |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
HTML::Seamstress - HTML::Tree subclass for HTML templating via tree rewriting |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head1 SYNOPSIS |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=head2 Text substitution via replace_content() API call. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
In our first example, we want to perform simple text substitution on |
124
|
|
|
|
|
|
|
the HTML template document. The HTML file html/hello_world.htm has |
125
|
|
|
|
|
|
|
klass attributes which serve as compiler (kompiler?) hints to Seamstress: |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
<html> |
128
|
|
|
|
|
|
|
<head> |
129
|
|
|
|
|
|
|
<title>Hello World</title> |
130
|
|
|
|
|
|
|
</head> |
131
|
|
|
|
|
|
|
<body> |
132
|
|
|
|
|
|
|
<h1>Hello World</h1> |
133
|
|
|
|
|
|
|
<p>Hello, my name is <span id="name">dummy_name</span>. |
134
|
|
|
|
|
|
|
<p>Today's date is <span id="date">dummy_date</span>. |
135
|
|
|
|
|
|
|
</body> |
136
|
|
|
|
|
|
|
</html> |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head3 Seamstress compiles HTML to C<html::hello_world> |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
shell> seamc html/hello_world.htm |
141
|
|
|
|
|
|
|
Seamstress v2.91 generating html::hello_world from html/hello_world.htm |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Now you simply use the "compiled" version of HTML with API calls to |
144
|
|
|
|
|
|
|
HTML::TreeBuilder, HTML::Element, and HTML::Element::LIbrary |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
use html::hello_world; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $tree = html::hello_world->new; |
149
|
|
|
|
|
|
|
$tree->look_down(id => name)->replace_content('terrence brannon'); |
150
|
|
|
|
|
|
|
$tree->look_down(id => date)->replace_content('5/11/1969'); |
151
|
|
|
|
|
|
|
print $tree->as_HTML; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head2 If-then-else with the highlander API call |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
(But also see C<< $tree->passover() >> in L<HTML::Element::Library>). |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
<span id="age_dialog"> |
158
|
|
|
|
|
|
|
<span id="under10"> |
159
|
|
|
|
|
|
|
Hello, does your mother know you're |
160
|
|
|
|
|
|
|
using her AOL account? |
161
|
|
|
|
|
|
|
</span> |
162
|
|
|
|
|
|
|
<span id="under18"> |
163
|
|
|
|
|
|
|
Sorry, you're not old enough to enter |
164
|
|
|
|
|
|
|
(and too dumb to lie about your age) |
165
|
|
|
|
|
|
|
</span> |
166
|
|
|
|
|
|
|
<span id="welcome"> |
167
|
|
|
|
|
|
|
Welcome |
168
|
|
|
|
|
|
|
</span> |
169
|
|
|
|
|
|
|
</span> |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head3 Compile and use the module: |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
use html::age_dialog; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
my $tree = html::dialog->new; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
$tree->highlander |
179
|
|
|
|
|
|
|
(age_dialog => |
180
|
|
|
|
|
|
|
[ |
181
|
|
|
|
|
|
|
under10 => sub { $_[0] < 10} , |
182
|
|
|
|
|
|
|
under18 => sub { $_[0] < 18} , |
183
|
|
|
|
|
|
|
welcome => sub { 1 } |
184
|
|
|
|
|
|
|
], |
185
|
|
|
|
|
|
|
$age |
186
|
|
|
|
|
|
|
); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
print $tree->as_HTML; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# will only output one of the 3 dialogues based on which closure |
191
|
|
|
|
|
|
|
# fires first |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
The following libraries are always available for more complicated |
195
|
|
|
|
|
|
|
manipulations: |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=over |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=item * L<HTML::ElementTable> |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item * L<HTML::Element::Library> |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item * L<HTML::Element> |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item * L<HTML::Tree> |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=back |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head1 PHILOSOPHY and MOTIVATION of HTML::Seamstress |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
Welcome to push-style dynamic HTML generation! |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
When looking at HTML::Seamstress, we are looking at a uniquely |
216
|
|
|
|
|
|
|
positioned 4th-generation HTML generator. Seamstress offers two sets |
217
|
|
|
|
|
|
|
of advantages: those common to all 4th generation htmlgens and those |
218
|
|
|
|
|
|
|
common to a subclass of L<HTML::Tree>. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
I think a Perlmonks node: |
223
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=669956> |
224
|
|
|
|
|
|
|
sums up the job of Seamstress quite well: |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Monks, |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
I'm tired of writing meta code in templating languages. |
229
|
|
|
|
|
|
|
I'm really good at writing Perl, and good at writing HTML, |
230
|
|
|
|
|
|
|
but I'm lousy at the templating languages (and I'm not too |
231
|
|
|
|
|
|
|
fired up to learn more about them). |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 Reap 4th generation dynamic HTML generation benefits |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
What advantages does this fourth way of HTML manipulation offer? Let's |
237
|
|
|
|
|
|
|
take a look: |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head3 Guarantee yourself well-formed HTML |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Because lower-generation dynamic HTML generators treat HTML as a |
242
|
|
|
|
|
|
|
string, there is no insurance against poorly formed HTML. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Take a look at these two Mason components, from |
245
|
|
|
|
|
|
|
L<http://masonbook.com/book/chapter-5.mhtml#TOC-ANCHOR-5> : |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=over |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=item * Example 5-3. /autohandler |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
<html> |
252
|
|
|
|
|
|
|
% $m->call_next; |
253
|
|
|
|
|
|
|
</html> |
254
|
|
|
|
|
|
|
<%method .body_tag> |
255
|
|
|
|
|
|
|
<%args> |
256
|
|
|
|
|
|
|
$bgcolor => 'white' |
257
|
|
|
|
|
|
|
$textcolor => 'black' |
258
|
|
|
|
|
|
|
</%args> |
259
|
|
|
|
|
|
|
<body onLoad="prepare_images( )" bgcolor="<% $bgcolor %>" text="<% $textcolor %>"> |
260
|
|
|
|
|
|
|
</%method> |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=item * Example 5-4. /important_advice.mas |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
<head><title>A Blue Page With Red Text</title></head> |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
<& SELF:.body_tag, bgcolor=>'blue', textcolor=>'red' &> |
267
|
|
|
|
|
|
|
Never put anything bigger than your elbow into your ear. |
268
|
|
|
|
|
|
|
</body> |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=back |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
There is nothing guaranteeing that open tags will match close tags or |
273
|
|
|
|
|
|
|
that close tags will even exist. |
274
|
|
|
|
|
|
|
To make the correspondence between open and close tags even more troublesome, |
275
|
|
|
|
|
|
|
they are in different files. And it is not easy for an HTML designer and/or |
276
|
|
|
|
|
|
|
design tool to manipulate things once they have been shredded apart |
277
|
|
|
|
|
|
|
like this. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
With the tree-based approach of Seamstress, the end tag will exist |
281
|
|
|
|
|
|
|
and it will match the open tag. Well-formedness is job 1 in tree-based |
282
|
|
|
|
|
|
|
HTML rewriting! |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head4 HTML will be properly escaped |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head3 Separate HTML development and its programmatic modification |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Software engineers refer to this as B<orthogonality>. |
292
|
|
|
|
|
|
|
The contents of the document remain legal HTML/XML that can be be |
293
|
|
|
|
|
|
|
developed using standard interactive design tools. The flow of control |
294
|
|
|
|
|
|
|
of the code remains separate from the page. Technologies that mix |
295
|
|
|
|
|
|
|
content and data in a single file result in code that is often |
296
|
|
|
|
|
|
|
difficult to understand and has trouble taking full advantage of the |
297
|
|
|
|
|
|
|
object oriented programming paradigm. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head3 Work at meta-level instead of object-level |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
The book "Godel, Escher, Bach: An Eternal Golden Braid" by Douglas R |
302
|
|
|
|
|
|
|
Hofstadter makes it clear what it means to operate at object-level as |
303
|
|
|
|
|
|
|
opposed to meta-level. When you buy into earlier-generation HTML |
304
|
|
|
|
|
|
|
generation systems you are working at object-level: you can only speak |
305
|
|
|
|
|
|
|
and act I<as> the HTML with no ability to speak I<about> the HTML. |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
Compare a bird's eye view of a city with standing on a city block and |
308
|
|
|
|
|
|
|
you have the difference between the 4th generation of HTML development |
309
|
|
|
|
|
|
|
versus all prior generations. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head3 Reduced learning curve |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
If you have a strong hold on |
315
|
|
|
|
|
|
|
object-oriented Perl and a solid understand of the tree-based nature |
316
|
|
|
|
|
|
|
of HTML, then all you need to do is read the manual pages showing how |
317
|
|
|
|
|
|
|
Seamstress and related modules offer tree manipulation routines and |
318
|
|
|
|
|
|
|
you are done. |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Extension just requires writing new Perl methods - a snap for any |
321
|
|
|
|
|
|
|
object oriented Perler. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=head3 Static validation and formatting |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Mixing Perl and HTML (by any of the generation 1-3 approaches) |
326
|
|
|
|
|
|
|
makes it impossible to use standard validation and formatting tools |
327
|
|
|
|
|
|
|
for either Perl or HTML. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head3 Two full-strength programming languages: HTML and Perl |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Perl and HTML are solid technologies with years of effort behind |
333
|
|
|
|
|
|
|
making them robust and flexible enough to meet real-world |
334
|
|
|
|
|
|
|
technological demands. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=head3 Object-oriented reuse and extension of HTML |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Class-based object-oriented programming makes use of inheritance and |
339
|
|
|
|
|
|
|
other techniques to achieve maximum code reuse. This typically |
340
|
|
|
|
|
|
|
happens by a certain base/superclass method containing common actions |
341
|
|
|
|
|
|
|
and a derived/subclass/mixin method containing extra actions. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
A genuine tree-based approach (such as HTML::Seamstress) to HTML |
344
|
|
|
|
|
|
|
generation is supportive of all methods of object-oriented reuse: |
345
|
|
|
|
|
|
|
because manipulator and manipulated are separate and manipulators are |
346
|
|
|
|
|
|
|
written in oo Perl, we can compose manipulators as we please. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
This is in contrast to inline simple object systems (as in Mason) and |
349
|
|
|
|
|
|
|
also in contrast to the if-then approach of tt-esque systems. |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=head4 Per-page stereotyped substitution |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
[ FYI: you can run the two Seamstress approaches. They are in |
354
|
|
|
|
|
|
|
F<$DISTRO/samples/perpage> ] |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
In the HTML::Mason book by O'Reilly: |
357
|
|
|
|
|
|
|
L<http://masonbook.com/book/chapter-1.mhtml#TOC-ANCHOR-4> |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
we see a technique for doing simple text insertion which varies per |
360
|
|
|
|
|
|
|
page: |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
<html> |
363
|
|
|
|
|
|
|
<head><title>Welcome to Wally World!</title></head> |
364
|
|
|
|
|
|
|
<body bgcolor="#CCFFCC"> |
365
|
|
|
|
|
|
|
<center><h1><% $m->base_comp->attr('head') %></h1></center> |
366
|
|
|
|
|
|
|
% $m->call_next; |
367
|
|
|
|
|
|
|
<center><a href="/">Home</a></center> |
368
|
|
|
|
|
|
|
</body></html> |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# homepage.html |
371
|
|
|
|
|
|
|
<%attr> |
372
|
|
|
|
|
|
|
head => "Wally World Home" |
373
|
|
|
|
|
|
|
</%attr> |
374
|
|
|
|
|
|
|
Here at Wally World you'll find all the finest accoutrements. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# productpage.html |
377
|
|
|
|
|
|
|
<%attr> |
378
|
|
|
|
|
|
|
head => "Wally World Products" |
379
|
|
|
|
|
|
|
</%attr> |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
<table> ... </table> |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
So, how would we do this using Seamstress' pure Perl approach to HTML |
384
|
|
|
|
|
|
|
refinement? |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
<html> |
388
|
|
|
|
|
|
|
<head><title>Welcome to Wally World!</title></head> |
389
|
|
|
|
|
|
|
<body bgcolor="#CCFFCC"> |
390
|
|
|
|
|
|
|
<center><h1 id=head>DUMMY_HEAD</h1></center> |
391
|
|
|
|
|
|
|
<span id=body>DUMMY_BODY</span> |
392
|
|
|
|
|
|
|
<center><a href="/">Home</a></center> |
393
|
|
|
|
|
|
|
</body></html> |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# homepage.pm |
396
|
|
|
|
|
|
|
package html::homepage; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
use base qw( HTML::Seamstress ) ; |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub new { |
401
|
|
|
|
|
|
|
my ($class, $c) = @_; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
my $html_file = 'html/base.html'; |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
my $tree = __PACKAGE__->new_from_file($html_file); |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
$tree; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
sub process { |
411
|
|
|
|
|
|
|
my ($tree, $c, $stash) = @_; |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
$tree->content_handler(head => 'Wally World Home'); |
414
|
|
|
|
|
|
|
$tree->content_handler(body => |
415
|
|
|
|
|
|
|
'Here at Wally World you'll find all the finest accoutrements.'); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# productpage.pm |
419
|
|
|
|
|
|
|
package html::productpage; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
use base qw( HTML::Seamstress ) ; |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub new { |
424
|
|
|
|
|
|
|
my ($class, $c) = @_; |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
my $html_file = 'html/base.html'; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
my $tree = __PACKAGE__->new_from_file($html_file); |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
$tree; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub process { |
434
|
|
|
|
|
|
|
my ($tree, $c, $stash) = @_; |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
$tree->content_handler(head => 'Wally World Products); |
437
|
|
|
|
|
|
|
$tree->content_handler(body => html::productpage::body->new->guts) |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
We have solved our problem. However, we can create even more re-use |
441
|
|
|
|
|
|
|
because the both of these classes are very similar. They only vary in |
442
|
|
|
|
|
|
|
2 things: the particular head and body they provide. |
443
|
|
|
|
|
|
|
You can abstract this with whatever methodmaker you like. I tend to |
444
|
|
|
|
|
|
|
prefer prototype-based oop |
445
|
|
|
|
|
|
|
over class-based, so with L<Class::Prototyped|Class::Prototyped>, |
446
|
|
|
|
|
|
|
here's how we might do it: |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
package html::abstract::common; |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
use base qw(HTML::Seamstress Class::Prototyped); |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub head { 'ABSTRACT BASE METHOD' } |
454
|
|
|
|
|
|
|
sub body { 'ABSTRACT BASE METHOD' } |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
__PACKAGE__->reflect->addSlots( |
457
|
|
|
|
|
|
|
html_file => 'html/base.html', |
458
|
|
|
|
|
|
|
); |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
sub new { |
461
|
|
|
|
|
|
|
my $self = shift; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
my $tree = $self->new_from_file($self->html_file); |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub process { |
467
|
|
|
|
|
|
|
my ($tree, $c, $stash) = @_; |
468
|
|
|
|
|
|
|
$tree->content_handler(head => $tree->head); |
469
|
|
|
|
|
|
|
$tree->content_handler(body => $tree->body); |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
1; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
and then have both of the above classes instantiate and |
475
|
|
|
|
|
|
|
specialize this common class accordingly. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
[ Again: you can run the two Seamstress approaches. They are in |
478
|
|
|
|
|
|
|
F<$DISTRO/samples/perpage> ] |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head3 Parallel generation of a single page natural |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
A tree of HTML usually contains subtrees with no |
485
|
|
|
|
|
|
|
inter-dependance. They therefore can be manipulated in parallel. If a |
486
|
|
|
|
|
|
|
page contains 5 areas each of which takes C<N> time, then one could |
487
|
|
|
|
|
|
|
realize an N-fold speedup. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=head2 Reap the benefits of using HTML::Tree |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=head3 Pragmatic HTML instead of strict X(HT)ML |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
The real world is unfortunately more about getting HTML to work with |
494
|
|
|
|
|
|
|
IE and maybe 1 or 2 other browsers. Strict XHTML may not be acceptable |
495
|
|
|
|
|
|
|
under time and corporate pressures to get things to work with quirky |
496
|
|
|
|
|
|
|
browsers. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head3 Rich API and User Contributions |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
L<HTML::Tree> has a nice large set of accessor/modifier functions. If |
501
|
|
|
|
|
|
|
that is not enough, then take a gander at Matthew Sisk's |
502
|
|
|
|
|
|
|
contributions: L<http://search.cpan.org/~msisk/> as well as |
503
|
|
|
|
|
|
|
L<HTML::Element::Library>. |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=head1 Seamstress contains no voodoo elements whatsoever |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
If you know object-oriented Perl and know how to rewrite trees, then |
508
|
|
|
|
|
|
|
everything that Seamstress offers will make sense: it's just various |
509
|
|
|
|
|
|
|
boilerplates and scripts that allow your mainline code to be very |
510
|
|
|
|
|
|
|
succinct: think of it as Class::DBI for HTML::Tree. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=over |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=item * unifying HTML and the HTML processing via a Perl class |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Seamstress contains two scripts, F<spkg.pl> and F<sbase.pl> which |
518
|
|
|
|
|
|
|
together make it easy to access and modify an HTML file in very few |
519
|
|
|
|
|
|
|
lines of startup code. If you have a file named |
520
|
|
|
|
|
|
|
F<html/hello_world.html>, Seamstress makes it easy for that to become |
521
|
|
|
|
|
|
|
the Perl module C<html::hello_world> with a C<new()> method that |
522
|
|
|
|
|
|
|
loads and parses the HTML into an L<HTML::Tree|HTML::Tree>. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=item * a Catalyst View class with meat-skeleton processing |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
The meat-skeleton HTML production concept is discussed below. |
527
|
|
|
|
|
|
|
L<Catalyst::Seamstress::View|Catalyst::Seamstress::View> is all ready |
528
|
|
|
|
|
|
|
to go for rendering simple or more complex pages. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=item * Loading in the HTML::Tree support classes |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
One a Perl class has been built for your HTML, it has |
533
|
|
|
|
|
|
|
L<HTML::Element|HTML::Element> and |
534
|
|
|
|
|
|
|
L<HTML::Element::Library|HTML::Element::Library> as superclasses, ready |
535
|
|
|
|
|
|
|
for you to use to rewrite the tree. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=back |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head2 Seamstress is here to help you use HTML::Tree, that's all. |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=head2 Unify HTML and the processing of the HTML via a Perl class |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
Let's see why this is a good idea. In Mason, your Perl and HTML are |
544
|
|
|
|
|
|
|
right there together in the same file. |
545
|
|
|
|
|
|
|
Same with Template. Now, since Seamstress |
546
|
|
|
|
|
|
|
operates on the HTML without touching the HTML, the operations and |
547
|
|
|
|
|
|
|
the HTML are not in the same file. So we create a Perl module to |
548
|
|
|
|
|
|
|
glue the HTML file to the operations we plan to perform on it. |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
This module (auto-created by F<spkg.pl> and perhaps F<sbase.pl>) |
551
|
|
|
|
|
|
|
has a constructor C<new()>, which grabs the HTML file and |
552
|
|
|
|
|
|
|
constructs an L<HTML::Element|HTML::Element> tree from it and |
553
|
|
|
|
|
|
|
returns it to you. |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
It also contains a C<process()> subroutine which processes the |
556
|
|
|
|
|
|
|
HTML in some way: text substitutions, unrolling list elements, |
557
|
|
|
|
|
|
|
building tables, and whatnot. |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
Finally, it contains a C<fixup()> subroutine. This subroutine is |
560
|
|
|
|
|
|
|
designed to support the meat-skeleton paradigm, discussed above. |
561
|
|
|
|
|
|
|
The C<process()> subroutine generated the C<$meat>. After <$meat> |
562
|
|
|
|
|
|
|
has been placed in C<$skeleton>, there may be some page-specific |
563
|
|
|
|
|
|
|
processing to the whole HTML page that you want to: pop in some |
564
|
|
|
|
|
|
|
javascript, remove a copyright notice, whatever. That's what |
565
|
|
|
|
|
|
|
this routine is for. |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
Now that I've said all that, please understand that you are perfectly |
568
|
|
|
|
|
|
|
free to call C<new()> and do what you want with the HTML tree. You |
569
|
|
|
|
|
|
|
don't have to use C<process()> and C<fixup()>. But they are there and |
570
|
|
|
|
|
|
|
are used by L<Catalyst::View::Seamstress> to make meat-skeleton |
571
|
|
|
|
|
|
|
dynamic HTML development quick-and-easy (and non-greasy). |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=head3 A Perl class created by spkg.pl |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Here is our venerable little HTML file: |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
metaperl@pool-71-109-151-76:/ernest/dev/catalyst-simpleapp/MyApp/root/html$ cat hello_world.html |
578
|
|
|
|
|
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
579
|
|
|
|
|
|
|
<html> |
580
|
|
|
|
|
|
|
<head> |
581
|
|
|
|
|
|
|
<title>Hello World</title> |
582
|
|
|
|
|
|
|
</head> |
583
|
|
|
|
|
|
|
<body> |
584
|
|
|
|
|
|
|
<h1>Hello World</h1> |
585
|
|
|
|
|
|
|
<p>Hello, my name is <span id="name">dummy_name</span>. |
586
|
|
|
|
|
|
|
<p>Today's date is <span id="date">dummy_date</span>. |
587
|
|
|
|
|
|
|
</body> |
588
|
|
|
|
|
|
|
</html> |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
Now let's abstract this as a Perl class: |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
metaperl@pool-71-109-151-76:/ernest/dev/catalyst-simpleapp/MyApp/root/html$ spkg.pl --base_pkg=MyApp::View::Seamstress --base_pkg_root=`pwd`/../../lib hello_world.html |
594
|
|
|
|
|
|
|
comp_root........ /ernest/dev/catalyst-simpleapp/MyApp/root/ |
595
|
|
|
|
|
|
|
html_file_path... /ernest/dev/catalyst-simpleapp/MyApp/root/html/ |
596
|
|
|
|
|
|
|
html_file........ hello_world.html |
597
|
|
|
|
|
|
|
html_file sans... hello_world |
598
|
|
|
|
|
|
|
hello_world.html compiled to package html::hello_world |
599
|
|
|
|
|
|
|
metaperl@pool-71-109-151-76:/ernest/dev/catalyst-simpleapp/MyApp/root/html$ |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
Now lets see what html::hello_world looks like. Everything other than |
602
|
|
|
|
|
|
|
C<process()> was auto-generated: |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
package html::hello_world; |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
use strict; |
607
|
|
|
|
|
|
|
use warnings; |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
use HTML::TreeBuilder; |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
use base qw(MyApp::View::Seamstress); |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
our $tree; |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub new { |
618
|
|
|
|
|
|
|
my $file = __PACKAGE__->comp_root() . 'html/hello_world.html' ; |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
-e $file or die "$file does not exist. Therefore cannot load"; |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
$tree =HTML::TreeBuilder->new; |
623
|
|
|
|
|
|
|
$tree->parse_file($file); |
624
|
|
|
|
|
|
|
$tree->eof; |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
bless $tree, __PACKAGE__; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
sub process { |
630
|
|
|
|
|
|
|
my ($self, $c, $stash) = @_; |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
$tree->look_down(id => $_)->replace_content($stash->{$_}) |
633
|
|
|
|
|
|
|
for qw(name date); |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
sub fixup { $tree } |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
1; |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
=head2 The meat-skeleton paradigm |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
This section is written to help understanding of |
645
|
|
|
|
|
|
|
L<Catalyst::View::Seamstress> for people who want to use Seamstress as |
646
|
|
|
|
|
|
|
the view for their L<Catalyst|Catalyst> apps. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
HTML pages typically have meat and a skeleton. The meat varies from page |
649
|
|
|
|
|
|
|
to page while the skeleton is fairly (though not completely) |
650
|
|
|
|
|
|
|
static. For example, the skeleton of a webpage is usually a header, a |
651
|
|
|
|
|
|
|
footer, and a navbar. The meat is what shows up when you click on a |
652
|
|
|
|
|
|
|
link on the page somewhere. While the meat will change with each |
653
|
|
|
|
|
|
|
click, the skeleton is rather static. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
The perfect example of |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Mason accomodates the meat-skeleton paradigm via |
658
|
|
|
|
|
|
|
an C<autohandler> and C<< $m->call_next() >>. Template |
659
|
|
|
|
|
|
|
accomodates it via its C<WRAPPER> directive. |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
And Seamstress? Well, here's what you _can_ do: |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=over |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=item 1 generate the meat, C<$meat> |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
This is typically what you see in the C<body> part of an HTML page |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=item 2 generate the skeleton, C<$skeleton> |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
This is typically the html, head, and maybe some body |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=item 3 put the meat in the skeleton |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=back |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
So, nothing about this is forced. This is just how I typically do |
678
|
|
|
|
|
|
|
things and that is why |
679
|
|
|
|
|
|
|
L<Catalyst::View::Seamstress|Catalyst::View::Seamstress> has support |
680
|
|
|
|
|
|
|
for this. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
In all honesty, the meat-skeleton paradigm should be supported here |
683
|
|
|
|
|
|
|
and called from C<Catalyst::View::Seamstress>. But the problem is, I |
684
|
|
|
|
|
|
|
don't |
685
|
|
|
|
|
|
|
want to create an abstract API here unless I have used the |
686
|
|
|
|
|
|
|
meat-skeleton paradigm from one other framework besides Catalyst. Then |
687
|
|
|
|
|
|
|
I will have a good idea of how to refactor it so any framework can |
688
|
|
|
|
|
|
|
make good use of the paradigm. |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=head1 USAGE |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
The best example of usage is the F<Quickstart> directory in this |
698
|
|
|
|
|
|
|
distribution. You can read L<HTML::Seamstress::Quickstart> and |
699
|
|
|
|
|
|
|
actually run the code in that directory at the same time. After doing |
700
|
|
|
|
|
|
|
so, the following sections are additional instruction. |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
=head2 Understand that HTML is a tree |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
The best representation of this fact is this slide right here: |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
L<http://xmlc.objectweb.org/doc/xmlcSlides/xmlcSlides.html#de> |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
If you understand this (and maybe the rest of the slides), then you |
710
|
|
|
|
|
|
|
have a good grip on seeing HTML as a tree. |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
L<HTML::Tree::AboutTrees> does also teach this, but it takes a while |
713
|
|
|
|
|
|
|
before he gets to what matters to us. It's a fun read nonetheless. |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
Now that we've got this concept under our belts let's try some full examples. |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 Install and Setup Seamstress |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
The first thing to remember is that Seamstress is really just |
720
|
|
|
|
|
|
|
convenience functions for L<HTML::Tree|HTML::Tree>. You can do |
721
|
|
|
|
|
|
|
entirely without |
722
|
|
|
|
|
|
|
Seamstress. It's just that my daily real-world obligations have lead |
723
|
|
|
|
|
|
|
to a set of library functions (HTML::Element::Library) and a |
724
|
|
|
|
|
|
|
convenient way to locate "templates" (C<spkg.pl>) that work well on |
725
|
|
|
|
|
|
|
top of L<HTML::Tree|HTML::Tree> |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=over |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=item * move spkg.pl and sbase.pl onto your execution C<$PATH> |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
C<sbase.pl> and C<spkg.pl> are used to simplify the process of |
732
|
|
|
|
|
|
|
parsing an HTML file into HTML::Treebuilder object. In other words |
733
|
|
|
|
|
|
|
instead of having to do this in your Perl programs: |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
use HTML::TreeBuilder; |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
my $tree = HTML::TreeBuilder->new_from_file('/usr/htdocs/hello.html'); |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
You can do this: |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
use htdocs::hello; |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
my $tree = htdocs::hello->new; |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
The lines of code is not much different, but abstracting away absolute |
746
|
|
|
|
|
|
|
paths is important in production environments where the absolute path |
747
|
|
|
|
|
|
|
may come from who knows where via who knows how. |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=item * run sbase.pl |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
sbase.pl will ask you 2 very simple questions. Just answer them. |
752
|
|
|
|
|
|
|
When it is finished, it will have installed a package named |
753
|
|
|
|
|
|
|
C<HTML::Seamstress::Base> on your C<@INC>. This module contains one |
754
|
|
|
|
|
|
|
function, C<comp_root()> which points to a place you wouldn't |
755
|
|
|
|
|
|
|
typically have on your C<@INC> but which you must have because your |
756
|
|
|
|
|
|
|
HTML file and corresponding C<.pm> abstracting it are going to be |
757
|
|
|
|
|
|
|
there. |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
=item * run spkg.pl |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
In the default seutp, |
762
|
|
|
|
|
|
|
no options need be supplied to this script. They |
763
|
|
|
|
|
|
|
are useful in cases where you have more than one document root or want |
764
|
|
|
|
|
|
|
to inherit from more than one place. |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
metaperl@pool-71-109-151-76:~/www$ spkg.pl moose.html |
768
|
|
|
|
|
|
|
comp_root........ /home/metaperl/ |
769
|
|
|
|
|
|
|
html_file_path... /home/metaperl/www/ |
770
|
|
|
|
|
|
|
html_file........ moose.html |
771
|
|
|
|
|
|
|
html_file sans... moose |
772
|
|
|
|
|
|
|
moose.html compiled to package www::moose |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=item * load your abstracted HTML and manipulate it |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
Now, from Perl, to get the TreeBuilder object |
777
|
|
|
|
|
|
|
representing this HTML file, we simply do this: |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
use www::moose; |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
my $tree = www::moose->new; |
782
|
|
|
|
|
|
|
# manipulate tree... |
783
|
|
|
|
|
|
|
$tree->as_HTML; |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
In a mod_perl setup, you would want to pre-load your HTML and |
786
|
|
|
|
|
|
|
L<Class::Cache|Class::Cache> was designed for this very purpose. But |
787
|
|
|
|
|
|
|
that's a topic for another time. |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
In a setup with HTML files in numerous places, I recommend setting up |
790
|
|
|
|
|
|
|
multiple C<HTML::Seamstress::Base::here>, |
791
|
|
|
|
|
|
|
C<HTML::Seamstress::Base::there> for each file root. To do this, you |
792
|
|
|
|
|
|
|
will need to use the C<--base_pkg> and C<--base_pkg_root> options to |
793
|
|
|
|
|
|
|
spkg.pl |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=item * That's it! |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
Now you are ready to abstract away as many files as you want with the |
799
|
|
|
|
|
|
|
same C<spkg.pl> call. Just supply it with a different HTML file to |
800
|
|
|
|
|
|
|
create a different package. Then C<use> them, C<new> them and |
801
|
|
|
|
|
|
|
manipulate them and C<< $tree->as_HTML >> them at will. |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
Now it's time to rock and roll! |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
=back |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=head2 Text substitution == node mutation |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
In our first example, we want to perform simple text substitution on |
812
|
|
|
|
|
|
|
the HTML template document: |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
<html> |
815
|
|
|
|
|
|
|
<head> |
816
|
|
|
|
|
|
|
<title>Hello World</title> |
817
|
|
|
|
|
|
|
</head> |
818
|
|
|
|
|
|
|
<body> |
819
|
|
|
|
|
|
|
<h1>Hello World</h1> |
820
|
|
|
|
|
|
|
<p>Hello, my name is <span id="name">dummy_name</span>. |
821
|
|
|
|
|
|
|
<p>Today's date is <span id="date">dummy_date</span>. |
822
|
|
|
|
|
|
|
</body> |
823
|
|
|
|
|
|
|
</html> |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
First save this somewhere on your document root. Then compile it with |
826
|
|
|
|
|
|
|
C<spkg.pl>. Now you simply use |
827
|
|
|
|
|
|
|
the "compiled" version of HTML with API calls to |
828
|
|
|
|
|
|
|
HTML::TreeBuilder, HTML::Element, and HTML::Element::Library. |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
use html::hello_world; |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
my $tree = html::hello_world->new; |
833
|
|
|
|
|
|
|
$tree->look_down(id => name)->replace_content('terrence brannon'); |
834
|
|
|
|
|
|
|
$tree->look_down(id => date)->replace_content('5/11/1969'); |
835
|
|
|
|
|
|
|
print $tree->as_HTML; |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
C<replace_content()> is a convenience function in |
838
|
|
|
|
|
|
|
L<HTML::Element::Library>. |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=head2 If-then-else == node(s) deletion |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
(But also see C<< $tree->passover() >> in L<HTML::Element::Library>). |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
<span id="age_dialog"> |
847
|
|
|
|
|
|
|
<span id="under10"> |
848
|
|
|
|
|
|
|
Hello, does your mother know you're |
849
|
|
|
|
|
|
|
using her AOL account? |
850
|
|
|
|
|
|
|
</span> |
851
|
|
|
|
|
|
|
<span id="under18"> |
852
|
|
|
|
|
|
|
Sorry, you're not old enough to enter |
853
|
|
|
|
|
|
|
(and too dumb to lie about your age) |
854
|
|
|
|
|
|
|
</span> |
855
|
|
|
|
|
|
|
<span id="welcome"> |
856
|
|
|
|
|
|
|
Welcome |
857
|
|
|
|
|
|
|
</span> |
858
|
|
|
|
|
|
|
</span> |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
Again, compile and use the module: |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
use html::age_dialog; |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
my $tree = html::dialog->new; |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
$tree->highlander |
868
|
|
|
|
|
|
|
(age_dialog => |
869
|
|
|
|
|
|
|
[ |
870
|
|
|
|
|
|
|
under10 => sub { $_[0] < 10} , |
871
|
|
|
|
|
|
|
under18 => sub { $_[0] < 18} , |
872
|
|
|
|
|
|
|
welcome => sub { 1 } |
873
|
|
|
|
|
|
|
], |
874
|
|
|
|
|
|
|
$age |
875
|
|
|
|
|
|
|
); |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
print $tree->as_HTML; |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
# will only output one of the 3 dialogues based on which closure |
880
|
|
|
|
|
|
|
# fires first |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
And once again, |
884
|
|
|
|
|
|
|
the function we used is the highlander method, also a part |
885
|
|
|
|
|
|
|
of L<HTML::Element::Library>. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
The following libraries are always available for more complicated |
889
|
|
|
|
|
|
|
manipulations: |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=over |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
=item * L<HTML::ElementSuper> |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=item * L<HTML::ElementTable> |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
=item * L<HTML::Element::Library> |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
=item * L<HTML::Element> |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
=item * L<HTML::Tree> |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
=back |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
=head2 Looping == child/sibling proliferation |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
Table unrolling, pulldown creation, C<li> unrolling, and C<dl> |
910
|
|
|
|
|
|
|
unrolling are |
911
|
|
|
|
|
|
|
all examples of a tree operation in which you take a child of a node |
912
|
|
|
|
|
|
|
and clone it and then alter it in some way (replace the content, alter |
913
|
|
|
|
|
|
|
some of its attributes), and then stick it under its parent. |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
Functions for use with the common HTML elements --- C<< <table> >>, |
916
|
|
|
|
|
|
|
C<< <ol> >>, |
917
|
|
|
|
|
|
|
C<< <ul> >>, C<< <dl> >>, C<< <select> >> |
918
|
|
|
|
|
|
|
are documented in |
919
|
|
|
|
|
|
|
L<HTML::Element::Library> and are |
920
|
|
|
|
|
|
|
prefaced with the words "Tree Building Methods". |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=head2 What Seamstress offers |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
Beyond the "compilation" support documented above, Seamstress offers |
926
|
|
|
|
|
|
|
nothing more than a simple structure-modifying method, |
927
|
|
|
|
|
|
|
expand_replace(). And to be honest, it probably shouldn't offer |
928
|
|
|
|
|
|
|
that. But once, when de-Mason-izing a site, it was easier to keep |
929
|
|
|
|
|
|
|
little itty-bitty components all over and so I wrote this method to |
930
|
|
|
|
|
|
|
facilitate the process. |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
Let's say you have this HTML: |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
<div id="sidebar"> |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
<div class="sideBlock" id=mpi>mc::picBar::index</div> |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
<div class="sideBlock" id=mnm>mc::navBox::makeLinks</div> |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
<div class="sideBlock" id=mg>mc::gutenBox</div> |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
</div> |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
In this case, the content of each sideBlock is the name of a Perl |
945
|
|
|
|
|
|
|
Seamstress-style class. As you know, when the constructor for such a |
946
|
|
|
|
|
|
|
class is called an |
947
|
|
|
|
|
|
|
HTML::Element, C<$E>, will be returned for it's parsed content. |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
In this case, we want the content of the div element to go from the |
950
|
|
|
|
|
|
|
being the class name to being the HTML::Element that the class |
951
|
|
|
|
|
|
|
constructs. So to inline all 3 tags you would do the following; |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
$tree->look_down(id => $_)->expand_replace for qw(mpi mnm mg); |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
=head2 What Seamstress works with |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
=head3 Class::Cache |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
Useful in mod_perl environments and anywhere you want control over the |
962
|
|
|
|
|
|
|
timing of object creation. |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
=head3 The family of HTML::Tree contributions |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
=over 4 |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
=item * L<HTML::ElementTable> |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
=item * L<HTML::Element::Library> |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
=item * L<HTML::Element> |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
=item * L<HTML::Tree> |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
=back |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
=head1 METHODS |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
=head2 ->new_from_file() |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
This does the same thing as the TreeBuilder C<new_from_file()> method, |
983
|
|
|
|
|
|
|
but it blesses the object into the invocant class. This makes the |
984
|
|
|
|
|
|
|
invocant class derive from Seamstress which means it has |
985
|
|
|
|
|
|
|
L<HTML::TreeBuilder|HTML::TreeBuilder>, |
986
|
|
|
|
|
|
|
L<HTML::Element|HTML::Element> , and |
987
|
|
|
|
|
|
|
L<HTML::Element::Library|HTML::Element::Library> at its disposal. |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
=head2 ->html() |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
This method takes C<__FILE__>, and optionally a desired C<$extension> |
992
|
|
|
|
|
|
|
(defaults to 'html' if not given) and |
993
|
|
|
|
|
|
|
changes the extension on C<__FILE__> from C<.pm> to C<$extension>. |
994
|
|
|
|
|
|
|
This works well for common situations. |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
=head1 A BRIEF HISTORY of Dynamic HTML Generation (Templating) |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
HTML::Seamstress provides "fourth generation" dynamic HTML generation |
999
|
|
|
|
|
|
|
(templating). |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
In the beginning we had... |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=head2 First generation dynamic HTML production - server side includes |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
First generation dynamic HTML production used server-side |
1007
|
|
|
|
|
|
|
includes: |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
<p>Today's date is <!--#echo var="DATE_LOCAL" --> </p> |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
=head2 Second generation dynamic HTML production - HTML in Perl |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
The next phase of HTML generation saw |
1014
|
|
|
|
|
|
|
embedded HTML snippets in Perl code. For example: |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
sub header { |
1017
|
|
|
|
|
|
|
my $title = shift; |
1018
|
|
|
|
|
|
|
print <<"EOHEADER"; |
1019
|
|
|
|
|
|
|
<head> |
1020
|
|
|
|
|
|
|
<title>$title</title> |
1021
|
|
|
|
|
|
|
</head> |
1022
|
|
|
|
|
|
|
EOHEADER |
1023
|
|
|
|
|
|
|
} |
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
=head2 Third generation dynamic HTML production - Perl/minilanguage in HTML |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
The 3rd generation solutions embed |
1028
|
|
|
|
|
|
|
programming language constructs with HTML. The language constructs |
1029
|
|
|
|
|
|
|
are either a real language (as is with L<HTML::Mason>) or a |
1030
|
|
|
|
|
|
|
pseudo/mini-language (as is with L<PeTaL>, L<Template> or |
1031
|
|
|
|
|
|
|
L<HTML::Template>). Let's see some L<Template> code: |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
<p>Hi there [% name %], are you enjoying your stay?</p> |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
=head2 Talkin' bout them generations... |
1036
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
Up to now, all approaches to this issue tamper with the |
1038
|
|
|
|
|
|
|
HTML in some form or fashion: |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
=over |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
=item * Generation 1 adds SSI processing instructions |
1043
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
=item * Generation 2 rips the HTML apart and adds programming elements |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
=item * Generation 3 sprinkles programming constructs in the HTML |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
=back |
1049
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
=head2 Enter fourth generation dynamic HTML production - DOM style |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
The fourth generation of HTML production is distinguished by no need |
1053
|
|
|
|
|
|
|
for tampering with the HTML. There are a wealth of XML-based modules |
1054
|
|
|
|
|
|
|
which provide this approach (L<XML::Twig>, L<XML::LibXML>, |
1055
|
|
|
|
|
|
|
L<XML::TreeBuilder>, L<XML::DOM>). HTML::Seamstress is the one CPAN |
1056
|
|
|
|
|
|
|
module based around HTML and L<HTML::Tree> for this approach. |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
The fourth generation is also the way that a language like Javascript rewrites HTML. |
1059
|
|
|
|
|
|
|
By using Seamstress, you can always think about manipulating your HTML in the same way! |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=head1 SEE ALSO |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
=head2 Object-oriented goodies |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
Seamstress is just glue for object-oriented tree processing in Perl (I can see my SEO rank climbing right now from that sentence!). |
1068
|
|
|
|
|
|
|
Anyway, here is your LOOM - (List of object-oriented modules): |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
=over 4 |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
=item * L<HTML::ELement::Replacer|HTML::Element::Replacer> |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
=item * L<HTML::ELement::Library|HTML::Element::Library> |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
=back |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
=head2 Related Software |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
I created a node at Perlmonks which catalogues push-style templating systems |
1081
|
|
|
|
|
|
|
both in and outside of Perl: |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=674225> |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
Here are two common ones: |
1086
|
|
|
|
|
|
|
L<http://xmlc.enhydra.org> |
1087
|
|
|
|
|
|
|
L<http://www.plope.com/software/meld3> |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
=over |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
=item * L<Template::Recall> |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
The author uses what he called "reverse callbacks" to create a style very |
1097
|
|
|
|
|
|
|
similar to Seamstress. |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
=item * L<Petal> |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
Based on Zope's TAL, this is a very nice and complete framework that is |
1102
|
|
|
|
|
|
|
the basis of MkDoc, a XML application server. It offers a |
1103
|
|
|
|
|
|
|
mini-language for XML rewriting, Seamstress does not. The philosophy |
1104
|
|
|
|
|
|
|
of the Seamstress is the orthogonal integration of Perl and HTML not a |
1105
|
|
|
|
|
|
|
mini-language and HTML. |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
=item * L<XML::LibXML> |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
By the XML guru Matt Sergeant, who is also the author of AxKit, another XML |
1110
|
|
|
|
|
|
|
application server. This offers XPath for finding nodes |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=item * L<XML::DOM> |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
If I wanted to ape XMLC entirely, I would have used TJ Mather's |
1115
|
|
|
|
|
|
|
L<XML::DOM>. Because XMLC is based around DOM API calls. However, |
1116
|
|
|
|
|
|
|
TreeBuilder is very handy and has a lot of nice libraries around it |
1117
|
|
|
|
|
|
|
such L<HTML::PrettyPrinter>. The biggest win of XML::DOM is it's easy |
1118
|
|
|
|
|
|
|
integration with L<XML::Generator> |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
From the docs, it looks like L<XML::GDOME> is the successor to this |
1121
|
|
|
|
|
|
|
module. |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
=back |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
=head2 Articles, Publications, Discussion |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head3 Push style templating systems |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
http://perlmonks.org/?node_id=674225 |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
=head3 Form Validation in CGI::Application with Seamstress |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=742427> |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
=head3 Easy table rendering in modern HTML::Seamstress |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=768430> |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
=head3 HTML Templating as Tree Rewriting: Part I: "If Statements" |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
L<http://perlmonks.org/index.pl?node_id=302606> |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
=head3 HTATR II: HTML table generation via DWIM tree rewriting |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
L<http://perlmonks.org/index.pl?node_id=303188> |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=head3 Survey of Surveys on HTML Templating systems |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=433729> |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
A fierce head-to-head between PeTaL and Seamstress goes on for several |
1156
|
|
|
|
|
|
|
days in this thread! |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
=head3 The disadvantages of mini-languages |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
The disadvantages of mini-languages is discussed here: |
1162
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=428053> |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
A striking example of the limitations of mini-languages is shown here: |
1165
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=493477> |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
But the most cogent argument for using full-strength languages as |
1168
|
|
|
|
|
|
|
opposed to mixing them occurs in the L<Text::Template> docs: |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
When people make a template module like this one, they almost always |
1171
|
|
|
|
|
|
|
start by inventing a special syntax for substitutions. For example, |
1172
|
|
|
|
|
|
|
they build it so that a string like %%VAR%% is replaced with the |
1173
|
|
|
|
|
|
|
value of $VAR. Then they realize the need extra formatting, so they |
1174
|
|
|
|
|
|
|
put in some special syntax for formatting. Then they need a loop, so |
1175
|
|
|
|
|
|
|
they invent a loop syntax. Pretty soon they have a new little |
1176
|
|
|
|
|
|
|
template language. |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
This approach has two problems: First, their little language is |
1179
|
|
|
|
|
|
|
crippled. If you need to do something the author hasn't thought of, |
1180
|
|
|
|
|
|
|
you lose. Second: Who wants to learn another language? You already |
1181
|
|
|
|
|
|
|
know Perl, so why not use it? |
1182
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
And for the Mason users whose retort is "we do use Perl!" the obvious |
1184
|
|
|
|
|
|
|
reply is: "granted, but in an embedded fashion with ad hoc, |
1185
|
|
|
|
|
|
|
inflexible object mechanisms, non-tree-based (hence syntactically |
1186
|
|
|
|
|
|
|
suspect) HTML manipulation, and no ability to statically validate the |
1187
|
|
|
|
|
|
|
Perl or HTML" |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
|
1190
|
|
|
|
|
|
|
=head3 Problems with JSP (JSP is similar to HTML::Mason) |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
L<http://www.servlets.com/soapbox/problems-jsp-reaction.html> |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
L<http://www-106.ibm.com/developerworks/library/w-friend.html?dwzone=web> |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
L<http://www.theserverside.com/resources/article.jsp?l=XMLCvsJSP> |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
=head3 Los Angeles Perl Mongers Talk on HTML::Seamstress |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
L<http://www.metaperl.org> |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
=head3 "Inside-out Templates in Perl" |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
L<http://www.webquills.net/web-development/perl/insideout-templates-in-perl.html> |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
=head1 SUPPORT and DEVELOPMENT |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
=head2 IRC |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
L<irc://irc.perl.org/#html-seamstress> |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=head2 Mailing List |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
L<http://lists.sourceforge.net/lists/listinfo/seamstress-discuss> |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
=head2 Source repo |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
L<http://github.com/metaperl/html-seamstress/tree/master> |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
=head1 AUTHOR |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
Terrence Brannon, C<< tbone@cpan.org >> |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
=head2 ACKNOWLEDGEMENTS |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
I would like to thank |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=over |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
=item * Chris Winters for exposing me to XMLC |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
=item * Paul Lucas for writing C<HTML_Tree> |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
L<http://homepage.mac.com/pauljlucas/software/html_tree/> |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
HTML_Tree is a C++ HTML manipulator with a Perl interface. Upon using |
1240
|
|
|
|
|
|
|
his Perl interface, I began to notice limitations and extended his |
1241
|
|
|
|
|
|
|
Perl interface. The author was not interested in working with me or my |
1242
|
|
|
|
|
|
|
extensions, so I had to continue on a separate path. |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
=item * C<johnnywang> for his post about dynamic HTML generation |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
L<http://perlmonks.org/?node_id=505080>. |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
=item * Matthew Sisk and John Porter for lively personal discussions |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
=item * Matthew Hodgson (Arathorn on #catalyst) |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
for brainstorming with me on how to produce a Catalyst view |
1253
|
|
|
|
|
|
|
for Seamstress |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
=item * Gary Ashton-Jones |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
for a patch to spkg.pl and being the first person to join |
1258
|
|
|
|
|
|
|
the C<seamstress-discuss> mailing list without any |
1259
|
|
|
|
|
|
|
solicitation from me C<:)>. |
1260
|
|
|
|
|
|
|
|
1261
|
|
|
|
|
|
|
=item * Brock Wilcox |
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
for ramming heads with me over possibly using CSS to specify tree |
1264
|
|
|
|
|
|
|
rewrite actions: |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
sub fix_age : ID(age) { |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
(shift)->replace_content(shift()) ; |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
} |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
Just an idea... |
1273
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
=item * Ian Tegebo |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
For noticing some doc bugs in the Quickstart guide. |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
=item * |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
=back |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
Copyright RANGE(1999,NOW()) by Terrence Brannon. |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
1288
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=cut |