a bit of refactor
[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 ();
7abc1b45 143
5802b80a 144 my $next_item_from_array = sub {
145 my @items = @_;
146 return sub { shift @items };
147 };
148
149 my $next_item_from_proto = sub {
150 my $proto = shift;
151 if (
152 ref $proto eq 'ARRAY' ||
153 ref $proto eq 'HASH' ||
154 Scalar::Util::blessed($proto)
155 ) {
156 return $next_item_from_array->($proto, @_);
157 } elsif(ref $proto eq 'CODE' ) {
158 return $proto;
159 } else {
160 die "Don't know what to do with $proto, it's a ". ref($proto);
161 }
162 };
163
164 my $normalize_targets = sub {
165 my $targets = shift;
166 my $targets_type = ref $targets;
7abc1b45 167 return $targets_type eq 'ARRAY' ? $targets : do {
168 $targets_type eq 'HASH' ? [ map { +{$_=>$targets->{$_}} } keys %$targets ]
5802b80a 169 : die "targets data structure ". ref($targets). " not understood";
7abc1b45 170 }
5802b80a 171 };
172
173 my $replace_from_hash_or_object = sub {
174 my ($datum, $value) = @_;
175 return ref($datum) eq 'HASH' ?
176 $datum->{$value} : $datum->$value;
177 };
178
179 my $fill = sub {
180 my ($zoom, $targets, @rest) = @_;
181 $zoom->repeat_content(sub {
182 my $itr = $next_item_from_proto->(@rest);
183 $targets = $normalize_targets->($targets);
184 HTML::Zoom::CodeStream->new({
185 code => sub {
186 my $cnt = 0;
187 if(my $datum = $itr->($zoom, $cnt)) {
188 $cnt++;
189 return sub {
190 for my $idx(0..$#{$targets}) {
191 my $target = $targets->[$idx];
192 my ($match, $replace) = do {
193 my $type = ref $target;
194 $type ? ($type eq 'HASH' ? do {
195 my ($match, $value) = %$target;
7abc1b45 196 ($match, ref($value) eq 'CODE' ?
197 $value->($datum, $idx, $_)
198 : $replace_from_hash_or_object->($datum, $value))
5802b80a 199 } : die "What?")
200 : do {
201 ref($datum) eq 'ARRAY' ?
202 ($target, $datum->[$idx]) :
203 ( '.'.$target, $replace_from_hash_or_object->($datum, $target));
204 };
205 };
206 $_ = $_->select($match)
207 ->replace_content($replace);
208 } $_;
209 };
210 } else {
211 return;
212 }
213 },
214 });
215 });
216 };
870c22a9 217
218 ## fill a selection from an array of targets, using a list. Syntax is a bit
219 ## like DBIC->populate
5802b80a 220
221 {
222 ok my $z = HTML::Zoom
223 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
224 ->select('ul')
225 ->$fill(
226 ['.even','.odd'],
227 [0,1],
228 [2,3],
229 ), 'Made Zoom object from array';
230
231 is(
232 $z->to_html,
233 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
234 'Got correct from repeat_content'
235 );
236 }
870c22a9 237
238 ## Same as above but using an iterator coderef instead of a pregenerated list
5802b80a 239 {
240 my @items = ( [0,1], [2,3] );
241 ok my $z = HTML::Zoom
242 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
243 ->select('ul')
244 ->$fill(
245 ['.even','.odd'],
246 sub { shift @items }
247 ), 'Made Zoom object from itr';
248
249 for my $cnt (1..2) {
250 is(
251 $z->to_html,
252 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
253 'Got correct from repeat_content: '.$cnt
254 );
255 ok(
256 (@items = ( [0,1], [2,3] )),
257 'Reset the items array'
258 );
259 }
260 }
870c22a9 261
262 ## Target has a value of a coderef that is expected to take the item from the
263 ## list of data and provide the actual value that goes into the target
5802b80a 264
265 {
266 ok my $z = HTML::Zoom
267 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
268 ->select('ul')
269 ->$fill(
270 [
271 {'.even' => sub { my ($i,$cnt,$z) = @_; return $i->[0]; } },
272 {'.odd' => sub { my ($i,$cnt,$z) = @_; return $i->[1]; } },
273 ],
274 [0,1],
275 [2,3],
276 ), 'Made Zoom object from code style';
277
278 for my $cnt (1..2) {
279 is(
280 $z->to_html,
281 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
282 'Got correct from repeat_content: '.$cnt
283 );
284 }
285 }
870c22a9 286
287 ## Like above but showing how this can be used to get a value from any type of
288 ## list item.
5802b80a 289
290 {
291 ok my $z = HTML::Zoom
292 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
293 ->select('ul')
294 ->$fill(
295 [
296 {'.even' => sub { my ($i,$cnt,$z) = @_; return $i->{even}; } },
297 {'.odd' => sub { my ($i,$cnt,$z) = @_; return $i->{odd}; } },
298 ],
299 { even => 0, odd => 1},
300 { even => 2, odd => 3},
301 ), 'Made Zoom object from code style';
302
303 for my $cnt (1..2) {
304 is(
305 $z->to_html,
306 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
307 'Got correct from repeat_content: '.$cnt
308 );
309 }
310 }
311
312 {
313 ok my $even_or_odd = sub {
314 my ($i,$cnt,$z) = @_;
315 return $cnt % 2 ? $i->{odd} : $i->{even};
316 };
317
318 ok my $z = HTML::Zoom
319 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
320 ->select('ul')
321 ->$fill(
322 [
323 {'.even' => $even_or_odd },
324 {'.odd' => $even_or_odd },
325 ],
326 { even => 0, odd => 1},
327 { even => 2, odd => 3},
328 ), 'Made Zoom object from code style';
329
330 for my $cnt (1..2) {
331 is(
332 $z->to_html,
333 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
334 'Got correct from repeat_content: '.$cnt
335 );
336 }
337 }
870c22a9 338
339 ## Just an example of above showing off how you can use Perl to generate the
340 ## needed structures.
5802b80a 341
342 {
343 ok my $even_or_odd = sub {
344 my ($i,$cnt,$z) = @_;
345 return $cnt % 2 ? $i->{odd} : $i->{even};
346 };
347
348 ok my $z = HTML::Zoom
349 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
350 ->select('ul')
351 ->$fill(
352 [
353 map { +{$_ => $even_or_odd} } qw(.even .odd),
354 ],
355 { even => 0, odd => 1},
356 { even => 2, odd => 3},
357 ), 'Made Zoom object from code style';
358
359 for my $cnt (1..2) {
360 is(
361 $z->to_html,
362 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
363 'Got correct from repeat_content: '.$cnt
364 );
365 }
366 }
870c22a9 367
368 ## using a list of objects, we get replace values.
5802b80a 369
370 {
371 {
372 package Test::HTML::Zoom::EvenOdd;
373
374 sub new {
375 my $class = shift;
376 bless { _e=>$_[0], _o=>$_[1] }, $class;
377 }
378
379 sub even { shift->{_e} }
380 sub odd { shift->{_o} }
381 }
382
383 ok my $even_or_odd = sub {
384 my ($i,$cnt,$z) = @_;
385 return $cnt % 2 ? $i->odd : $i->even;
386 };
387
388 ok my $z = HTML::Zoom
389 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
390 ->select('ul')
391 ->$fill(
392 [
393 map { +{$_ => $even_or_odd} } qw(.even .odd),
394 ],
395 Test::HTML::Zoom::EvenOdd->new(0,1),
396 Test::HTML::Zoom::EvenOdd->new(2,3),
397 ), 'Made Zoom object from code style';
398
399 for my $cnt (1..2) {
400 is(
401 $z->to_html,
402 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
403 'Got correct from repeat_content: '.$cnt
404 );
405 }
406 }
407
870c22a9 408 ## Looks a lot like how pure.js uses 'directives' to map a selector to a value
409 ## from each item in the data list.
410
5802b80a 411 {
412 ok my $z = HTML::Zoom
413 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
414 ->select('ul')
415 ->$fill(
416 [
417 { '.even' => 'even'},
418 { '.odd' => 'odd'},
419 ],
420 { even => 0, odd => 1},
421 { even => 2, odd => 3},
422 ), 'Made Zoom object from declare accessors style';
423
424 for my $cnt (1..2) {
425 is(
426 $z->to_html,
427 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
428 'Got correct from repeat_content: '.$cnt
429 );
430 }
431 }
432
433 {
434 ok my $z = HTML::Zoom
435 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
436 ->select('ul')
437 ->$fill(
438 [
439 { '.even' => 'even'},
440 { '.odd' => 'odd'},
441 ],
442 Test::HTML::Zoom::EvenOdd->new(0,1),
443 Test::HTML::Zoom::EvenOdd->new(2,3),
444 ), 'Made Zoom object from declare accessors style';
445
446 for my $cnt (1..2) {
447 is(
448 $z->to_html,
449 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
450 'Got correct from repeat_content: '.$cnt
451 );
452 }
453 }
454
870c22a9 455 ## Ideally the targets are an array so that you properly control the order that
456 ## the replaces happen, but if you don't care you can take advantage of the
457 ## shorthand target structure of a hashref
458
5802b80a 459 {
460 ok my $z = HTML::Zoom
461 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
462 ->select('ul')
463 ->$fill(
464 {
465 '.even' => 'even',
466 '.odd' => 'odd'
467 },
468 Test::HTML::Zoom::EvenOdd->new(0,1),
469 Test::HTML::Zoom::EvenOdd->new(2,3),
470 ), 'Made Zoom object from declare accessors style';
471
472 for my $cnt (1..2) {
473 is(
474 $z->to_html,
475 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
476 'Got correct from repeat_content: '.$cnt
477 );
478 }
479 }
480
481 ## Helper for when you have a list of object or hashs and you just want to map
482 ## target classes to accessors
483
484 {
485 ok my $z = HTML::Zoom
486 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
487 ->select('ul')
488 ->$fill(
489 { map { ".".$_ => $_ } qw(even odd) },
490 Test::HTML::Zoom::EvenOdd->new(0,1),
491 Test::HTML::Zoom::EvenOdd->new(2,3),
492 ), 'Made Zoom object from declare accessors style';
493
494 for my $cnt (1..2) {
495 is(
496 $z->to_html,
497 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
498 'Got correct from repeat_content: '.$cnt
499 );
500 }
501 }
502
503 ## if the target is arrayref but the data is a list of objects or hashes, we
504 ## guess the target is a class form of the target items, but use the value of
505 ## the item as the hash or object accessor
506
507 {
508 ok my $z = HTML::Zoom
509 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
510 ->select('ul')
511 ->$fill(
512 [qw(even odd)],
513 Test::HTML::Zoom::EvenOdd->new(0,1),
514 Test::HTML::Zoom::EvenOdd->new(2,3),
515 ), 'Made Zoom object from declare accessors style';
516
517 for my $cnt (1..2) {
518 is(
519 $z->to_html,
520 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
521 'Got correct from repeat_content: '.$cnt
522 );
523 }
524 }
870c22a9 525
526 ## Using above shortcut with iterator instead of fixed list
527
528 {
529 my @list = (
530 Test::HTML::Zoom::EvenOdd->new(0,1),
531 Test::HTML::Zoom::EvenOdd->new(2,3),
532 );
533
534 ok my $z = HTML::Zoom
535 ->from_html(q[<ul><li class="even">Even</li><li class="odd">Odd</li></ul>])
536 ->select('ul')
537 ->$fill(
538 [qw(even odd)],
539 sub { shift @list },
540 ), 'Made Zoom object from declare accessors style';
541
542 is(
543 $z->to_html,
544 '<ul><li class="even">0</li><li class="odd">1</li><li class="even">2</li><li class="odd">3</li></ul>',
545 'Got correct from repeat_content',
546 );
547 }
5802b80a 548}
549
b9c27a5a 550done_testing;