added some docs to each test point as a start to the eventual pod docs and to help...
[catagits/HTML-Zoom.git] / t / fill.t
CommitLineData
b9c27a5a 1use strict;
2use warnings FATAL => 'all';
3
4use HTML::Zoom;
5use Test::More;
6
7ok my $zoom = HTML::Zoom->from_html(<<HTML);
8<html>
9 <head>
10 <title>Hi!</title>
11 </head>
12 <body id="content-area">
13 <h1>Test</h1>
14 <div>
15 <p class="first-para">Some Stuff</p>
16 <p class="body-para">More Stuff</p>
17 <p class="body-para">Even More Stuff</p>
18 <ol>
19 <li class="first-item odd">First</li>
20 <li class="body-items even">Stuff A</li>
21 <li class="body-items odd">Stuff B</li>
22 <li class="body-items even">Stuff C</li>
23 <li class="last-item odd">Last</li>
24 </ol>
25 <ul class="items">
26 <li class="first">space</li>
27 <li class="second">space</li>
28 </ul>
29 <p class="body-para">Even More stuff</p>
30 <p class="last-para">Some Stuff</p>
31 </div>
32 <div id="blocks">
33 <h2 class="h2-item">Sub Item</h2>
34 </div>
35 <ul id="people">
36 <li class="name">name</li>
37 <li class="email">email</li>
38 </ul>
39 <ul id="people2">
40 <li class="name">name</li>
41 <li class="email">email</li>
42 </ul>
43 <ul id="object">
44 <li class="aa">AA</li>
45 <li class="bb">BB</li>
46 </ul>
47 <p id="footer">Copyright 2222</p>
48 </body>
49</html>
50HTML
51
52## Stub for testing the fill method to be
53
54ok my $title = sub {
55 my ($z, $content) = @_;
56 $z = $z->select('title')->replace_content($content);
57 return $z;
58};
59
5802b80a 60{
61 ok my $z = HTML::Zoom
62 ->from_html(q[<ul><li>Test</li></ul>])
63 ->select('ul')
64 ->repeat_content(
65 [
66 sub { $_->select('li')->replace_content('Real Life1') },
67 sub { $_->select('li')->replace_content('Real Life2') },
68 sub { $_->select('li')->replace_content('Real Life3') },
69 ],
70 )
71 ->to_html;
72
73 is $z, '<ul><li>Real Life1</li><li>Real Life2</li><li>Real Life3</li></ul>',
74 'Got correct from repeat_content';
75}
76
77{
78 ok my $z = HTML::Zoom
79 ->from_html(q[<ul><li>Test</li></ul>])
80 ->select('ul')
81 ->repeat_content([
82 map { my $i = $_; +sub {$_->select('li')->replace_content("Real Life$i")} } (1,2,3)
83 ])
84 ->to_html;
85
86 is $z, '<ul><li>Real Life1</li><li>Real Life2</li><li>Real Life3</li></ul>',
87 'Got correct from repeat_content';
88}
89
90
91use HTML::Zoom::CodeStream;
92sub code_stream (&) {
93 my $code = shift;
94 return sub {
95 HTML::Zoom::CodeStream->new({
96 code => $code,
97 });
98 }
99}
100
101{
102 my @list = qw(foo bar baz);
103 ok my $z = HTML::Zoom
104 ->from_html(q[<ul><li>Test</li></ul>])
105 ->select('ul')
106 ->repeat_content(code_stream {
107 if (my $name = shift @list) {
108 return sub { $_->select('li')->replace_content($name) };
109 } else {
110 return
111 }
112 })
113 ->to_html;
114
115 is $z, '<ul><li>foo</li><li>bar</li><li>baz</li></ul>',
116 'Got correct from repeat_content';
117}
118
119{
120 my @list = qw(foo bar baz);
121 ok my $z = HTML::Zoom
122 ->from_html(q[<ul><li>Test</li></ul>])
123 ->select('ul')
124 ->repeat_content(sub {
125 HTML::Zoom::CodeStream->new({
126 code => sub {
127 if (my $name = shift @list) {
128 return sub { $_->select('li')->replace_content($name) };
129 } else {
130 return
131 }
132 }
133 });
134 })
135 ->to_html;
136
137 is $z, '<ul><li>foo</li><li>bar</li><li>baz</li></ul>',
138 'Got correct from repeat_content';
139}
140
141{
142 use Scalar::Util ();
143 my $next_item_from_array = sub {
144 my @items = @_;
145 return sub { shift @items };
146 };
147
148 my $next_item_from_proto = sub {
149 my $proto = shift;
150 if (
151 ref $proto eq 'ARRAY' ||
152 ref $proto eq 'HASH' ||
153 Scalar::Util::blessed($proto)
154 ) {
155 return $next_item_from_array->($proto, @_);
156 } elsif(ref $proto eq 'CODE' ) {
157 return $proto;
158 } else {
159 die "Don't know what to do with $proto, it's a ". ref($proto);
160 }
161 };
162
163 my $normalize_targets = sub {
164 my $targets = shift;
165 my $targets_type = ref $targets;
166 return $targets_type eq 'ARRAY' ? $targets
167 : $targets_type eq 'HASH' ? [ map { +{$_=>$targets->{$_}} } keys %$targets ]
168 : die "targets data structure ". ref($targets). " not understood";
169 };
170
171 my $replace_from_hash_or_object = sub {
172 my ($datum, $value) = @_;
173 return ref($datum) eq 'HASH' ?
174 $datum->{$value} : $datum->$value;
175 };
176
177 my $fill = sub {
178 my ($zoom, $targets, @rest) = @_;
179 $zoom->repeat_content(sub {
180 my $itr = $next_item_from_proto->(@rest);
181 $targets = $normalize_targets->($targets);
182 HTML::Zoom::CodeStream->new({
183 code => sub {
184 my $cnt = 0;
185 if(my $datum = $itr->($zoom, $cnt)) {
186 $cnt++;
187 return sub {
188 for my $idx(0..$#{$targets}) {
189 my $target = $targets->[$idx];
190 my ($match, $replace) = do {
191 my $type = ref $target;
192 $type ? ($type eq 'HASH' ? do {
193 my ($match, $value) = %$target;
194 ref $value eq 'CODE' ?
195 ($match, $value->($datum, $idx, $_))
196 : ($match, $replace_from_hash_or_object->($datum, $value));
197 } : die "What?")
198 : do {
199 ref($datum) eq 'ARRAY' ?
200 ($target, $datum->[$idx]) :
201 ( '.'.$target, $replace_from_hash_or_object->($datum, $target));
202 };
203 };
204 $_ = $_->select($match)
205 ->replace_content($replace);
206 } $_;
207 };
208 } else {
209 return;
210 }
211 },
212 });
213 });
214 };
870c22a9 215
216 ## fill a selection from an array of targets, using a list. Syntax is a bit
217 ## like DBIC->populate
5802b80a 218
219 {
220 ok my $z = HTML::Zoom
221 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
222 ->select('ul')
223 ->$fill(
224 ['.even','.odd'],
225 [0,1],
226 [2,3],
227 ), 'Made Zoom object from array';
228
229 is(
230 $z->to_html,
231 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
232 'Got correct from repeat_content'
233 );
234 }
870c22a9 235
236 ## Same as above but using an iterator coderef instead of a pregenerated list
5802b80a 237 {
238 my @items = ( [0,1], [2,3] );
239 ok my $z = HTML::Zoom
240 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
241 ->select('ul')
242 ->$fill(
243 ['.even','.odd'],
244 sub { shift @items }
245 ), 'Made Zoom object from itr';
246
247 for my $cnt (1..2) {
248 is(
249 $z->to_html,
250 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
251 'Got correct from repeat_content: '.$cnt
252 );
253 ok(
254 (@items = ( [0,1], [2,3] )),
255 'Reset the items array'
256 );
257 }
258 }
870c22a9 259
260 ## Target has a value of a coderef that is expected to take the item from the
261 ## list of data and provide the actual value that goes into the target
5802b80a 262
263 {
264 ok my $z = HTML::Zoom
265 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
266 ->select('ul')
267 ->$fill(
268 [
269 {'.even' => sub { my ($i,$cnt,$z) = @_; return $i->[0]; } },
270 {'.odd' => sub { my ($i,$cnt,$z) = @_; return $i->[1]; } },
271 ],
272 [0,1],
273 [2,3],
274 ), 'Made Zoom object from code style';
275
276 for my $cnt (1..2) {
277 is(
278 $z->to_html,
279 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
280 'Got correct from repeat_content: '.$cnt
281 );
282 }
283 }
870c22a9 284
285 ## Like above but showing how this can be used to get a value from any type of
286 ## list item.
5802b80a 287
288 {
289 ok my $z = HTML::Zoom
290 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
291 ->select('ul')
292 ->$fill(
293 [
294 {'.even' => sub { my ($i,$cnt,$z) = @_; return $i->{even}; } },
295 {'.odd' => sub { my ($i,$cnt,$z) = @_; return $i->{odd}; } },
296 ],
297 { even => 0, odd => 1},
298 { even => 2, odd => 3},
299 ), 'Made Zoom object from code style';
300
301 for my $cnt (1..2) {
302 is(
303 $z->to_html,
304 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
305 'Got correct from repeat_content: '.$cnt
306 );
307 }
308 }
309
310 {
311 ok my $even_or_odd = sub {
312 my ($i,$cnt,$z) = @_;
313 return $cnt % 2 ? $i->{odd} : $i->{even};
314 };
315
316 ok my $z = HTML::Zoom
317 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
318 ->select('ul')
319 ->$fill(
320 [
321 {'.even' => $even_or_odd },
322 {'.odd' => $even_or_odd },
323 ],
324 { even => 0, odd => 1},
325 { even => 2, odd => 3},
326 ), 'Made Zoom object from code style';
327
328 for my $cnt (1..2) {
329 is(
330 $z->to_html,
331 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
332 'Got correct from repeat_content: '.$cnt
333 );
334 }
335 }
870c22a9 336
337 ## Just an example of above showing off how you can use Perl to generate the
338 ## needed structures.
5802b80a 339
340 {
341 ok my $even_or_odd = sub {
342 my ($i,$cnt,$z) = @_;
343 return $cnt % 2 ? $i->{odd} : $i->{even};
344 };
345
346 ok my $z = HTML::Zoom
347 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
348 ->select('ul')
349 ->$fill(
350 [
351 map { +{$_ => $even_or_odd} } qw(.even .odd),
352 ],
353 { even => 0, odd => 1},
354 { even => 2, odd => 3},
355 ), 'Made Zoom object from code style';
356
357 for my $cnt (1..2) {
358 is(
359 $z->to_html,
360 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
361 'Got correct from repeat_content: '.$cnt
362 );
363 }
364 }
870c22a9 365
366 ## using a list of objects, we get replace values.
5802b80a 367
368 {
369 {
370 package Test::HTML::Zoom::EvenOdd;
371
372 sub new {
373 my $class = shift;
374 bless { _e=>$_[0], _o=>$_[1] }, $class;
375 }
376
377 sub even { shift->{_e} }
378 sub odd { shift->{_o} }
379 }
380
381 ok my $even_or_odd = sub {
382 my ($i,$cnt,$z) = @_;
383 return $cnt % 2 ? $i->odd : $i->even;
384 };
385
386 ok my $z = HTML::Zoom
387 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
388 ->select('ul')
389 ->$fill(
390 [
391 map { +{$_ => $even_or_odd} } qw(.even .odd),
392 ],
393 Test::HTML::Zoom::EvenOdd->new(0,1),
394 Test::HTML::Zoom::EvenOdd->new(2,3),
395 ), 'Made Zoom object from code style';
396
397 for my $cnt (1..2) {
398 is(
399 $z->to_html,
400 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
401 'Got correct from repeat_content: '.$cnt
402 );
403 }
404 }
405
870c22a9 406 ## Looks a lot like how pure.js uses 'directives' to map a selector to a value
407 ## from each item in the data list.
408
5802b80a 409 {
410 ok my $z = HTML::Zoom
411 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
412 ->select('ul')
413 ->$fill(
414 [
415 { '.even' => 'even'},
416 { '.odd' => 'odd'},
417 ],
418 { even => 0, odd => 1},
419 { even => 2, odd => 3},
420 ), 'Made Zoom object from declare accessors style';
421
422 for my $cnt (1..2) {
423 is(
424 $z->to_html,
425 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
426 'Got correct from repeat_content: '.$cnt
427 );
428 }
429 }
430
431 {
432 ok my $z = HTML::Zoom
433 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
434 ->select('ul')
435 ->$fill(
436 [
437 { '.even' => 'even'},
438 { '.odd' => 'odd'},
439 ],
440 Test::HTML::Zoom::EvenOdd->new(0,1),
441 Test::HTML::Zoom::EvenOdd->new(2,3),
442 ), 'Made Zoom object from declare accessors style';
443
444 for my $cnt (1..2) {
445 is(
446 $z->to_html,
447 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
448 'Got correct from repeat_content: '.$cnt
449 );
450 }
451 }
452
870c22a9 453 ## Ideally the targets are an array so that you properly control the order that
454 ## the replaces happen, but if you don't care you can take advantage of the
455 ## shorthand target structure of a hashref
456
5802b80a 457 {
458 ok my $z = HTML::Zoom
459 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
460 ->select('ul')
461 ->$fill(
462 {
463 '.even' => 'even',
464 '.odd' => 'odd'
465 },
466 Test::HTML::Zoom::EvenOdd->new(0,1),
467 Test::HTML::Zoom::EvenOdd->new(2,3),
468 ), 'Made Zoom object from declare accessors style';
469
470 for my $cnt (1..2) {
471 is(
472 $z->to_html,
473 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
474 'Got correct from repeat_content: '.$cnt
475 );
476 }
477 }
478
479 ## Helper for when you have a list of object or hashs and you just want to map
480 ## target classes to accessors
481
482 {
483 ok my $z = HTML::Zoom
484 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
485 ->select('ul')
486 ->$fill(
487 { map { ".".$_ => $_ } qw(even odd) },
488 Test::HTML::Zoom::EvenOdd->new(0,1),
489 Test::HTML::Zoom::EvenOdd->new(2,3),
490 ), 'Made Zoom object from declare accessors style';
491
492 for my $cnt (1..2) {
493 is(
494 $z->to_html,
495 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
496 'Got correct from repeat_content: '.$cnt
497 );
498 }
499 }
500
501 ## if the target is arrayref but the data is a list of objects or hashes, we
502 ## guess the target is a class form of the target items, but use the value of
503 ## the item as the hash or object accessor
504
505 {
506 ok my $z = HTML::Zoom
507 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
508 ->select('ul')
509 ->$fill(
510 [qw(even odd)],
511 Test::HTML::Zoom::EvenOdd->new(0,1),
512 Test::HTML::Zoom::EvenOdd->new(2,3),
513 ), 'Made Zoom object from declare accessors style';
514
515 for my $cnt (1..2) {
516 is(
517 $z->to_html,
518 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
519 'Got correct from repeat_content: '.$cnt
520 );
521 }
522 }
870c22a9 523
524 ## Using above shortcut with iterator instead of fixed list
525
526 {
527 my @list = (
528 Test::HTML::Zoom::EvenOdd->new(0,1),
529 Test::HTML::Zoom::EvenOdd->new(2,3),
530 );
531
532 ok my $z = HTML::Zoom
533 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
534 ->select('ul')
535 ->$fill(
536 [qw(even odd)],
537 sub { shift @list },
538 ), 'Made Zoom object from declare accessors style';
539
540 is(
541 $z->to_html,
542 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
543 'Got correct from repeat_content',
544 );
545 }
5802b80a 546}
547
b9c27a5a 548done_testing;