File Coverage

blib/lib/UnderscoreJS.pm
Criterion Covered Total %
statement 570 579 98.4
branch 160 188 85.1
condition 43 65 66.1
subroutine 162 167 97.0
pod 0 108 0.0
total 935 1107 84.4


line stmt bran cond sub pod time code
1             package UnderscoreJS;
2              
3 7     7   887820 use strict;
  7         17  
  7         224  
4 7     7   36 use warnings;
  7         12  
  7         240  
5              
6             our $VERSION = '0.07';
7              
8 7     7   35 use B ();
  7         17  
  7         90  
9 7     7   6215 use List::MoreUtils ();
  7         8576  
  7         136  
10 7     7   46 use List::Util ();
  7         13  
  7         92  
11 7     7   35 use Scalar::Util ();
  7         13  
  7         427  
12              
13             our $UNIQUE_ID = 0;
14              
15             sub import {
16 7     7   61 my $class = shift;
17 7         14 my (%options) = @_;
18              
19 7   100     69 my $name = $options{-as} || '_';
20              
21 7         16 my $package = caller;
22 7     7   34 no strict;
  7         12  
  7         38444  
23 7         15 *{"$package\::$name"} = \&_;
  7         27769  
24             }
25              
26             sub _ {
27 365     365   247026 return new(__PACKAGE__, args => [@_]);
28             }
29              
30             sub new {
31 365     365 0 472 my $class = shift;
32              
33 365         784 my $self = {@_};
34 365         690 bless $self, $class;
35              
36 365         2041 $self->{template_settings} = {
37             evaluate => qr/<\%([\s\S]+?)\%>/,
38             interpolate => qr/<\%=([\s\S]+?)\%>/
39             };
40              
41 365         1788 return $self;
42             }
43              
44 8     8 0 40 sub true { UnderscoreJS::_True->new }
45 6     6 0 30 sub false { UnderscoreJS::_False->new }
46              
47 1     1 0 3 sub forEach {&each}
48              
49             sub each {
50 5     5 0 6 my $self = shift;
51 5         14 my ($array, $cb, $context) = $self->_prepare(@_);
52              
53 5 100       11 return unless defined $array;
54              
55 4 100       10 $context = $array unless defined $context;
56              
57 4         5 my $i = 0;
58 4         9 foreach (@$array) {
59 12         32 $cb->($_, $i, $context);
60 12         1195 $i++;
61             }
62             }
63              
64 1     1 0 2 sub collect {&map}
65              
66             sub map {
67 10     10 0 17 my $self = shift;
68 10         21 my ($array, $cb, $context) = $self->_prepare(@_);
69              
70 10 100       24 $context = $array unless defined $context;
71              
72 10         13 my $index = 0;
73 10         22 my $result = [map { $cb->($_, ++$index, $context) } @$array];
  34         229  
74              
75 10         109 return $self->_finalize($result);
76             }
77              
78 1     1 0 3 sub contains {&include}
79              
80             sub include {
81 7     7 0 11 my $self = shift;
82 7         13 my ($list, $value) = $self->_prepare(@_);
83              
84 7 100       21 if (ref $list eq 'ARRAY') {
    50          
85 6 100   13   28 return (List::Util::first { $_ eq $value } @$list) ? 1 : 0;
  13         42  
86             }
87             elsif (ref $list eq 'HASH') {
88 1 50   3   7 return (List::Util::first { $_ eq $value } values %$list) ? 1 : 0;
  3         13  
89             }
90              
91 0         0 die 'include only supports arrays and hashes';
92             }
93              
94 1     1 0 7 sub inject {&reduce}
95 0     0 0 0 sub foldl {&reduce}
96              
97             sub reduce {
98 22     22 0 32 my $self = shift;
99 22         48 my ($array, $iterator, $memo, $context) = $self->_prepare(@_);
100              
101 22 100 66     82 die 'No list or memo' if !defined $array && !defined $memo;
102              
103 21 100       52 return $memo unless defined $array;
104              
105 20         29 my $initial = defined $memo;
106              
107 20         32 foreach (@$array) {
108 388 100 100     1531 if (!$initial && defined $_) {
109 3         5 $memo = $_;
110 3         5 $initial = 1;
111             } else {
112 385 100       901 $memo = $iterator->($memo, $_, $context) if defined $_;
113             }
114             }
115 20 50       86 die 'No memo' if !$initial;
116 20         47 return $self->_finalize($memo);
117             }
118              
119 2     2 0 5 sub foldr {&reduce_right}
120 3     3 0 7 sub reduceRight {&reduce_right}
121              
122             sub reduce_right {
123 5     5 0 6 my $self = shift;
124 5         12 my ($array, $iterator, $memo, $context) = $self->_prepare(@_);
125              
126 5 100 66     22 die 'No list or memo' if !defined $array && !defined $memo;
127              
128 4 100       16 return $memo unless defined $array;
129              
130 3         5 return _->reduce([reverse @$array], $iterator, $memo, $context);
131             }
132              
133 1     1 0 3 sub find {&detect}
134              
135             sub detect {
136 2     2 0 2 my $self = shift;
137 2         6 my ($list, $iterator, $context) = $self->_prepare(@_);
138              
139 2     4   11 return List::Util::first { $iterator->($_) } @$list;
  4         18  
140             }
141              
142 1     1 0 3 sub filter {&select}
143              
144             sub select {
145 4     4 0 5 my $self = shift;
146 4         12 my ($list, $iterator, $context) = $self->_prepare(@_);
147              
148 4         9 my $result = [grep { $iterator->($_) } @$list];
  25         99  
149              
150 4         28 $self->_finalize($result);
151             }
152              
153             sub reject {
154 2     2 0 5 my $self = shift;
155 2         6 my ($list, $iterator, $context) = $self->_prepare(@_);
156              
157 2         6 my $result = [grep { !$iterator->($_) } @$list];
  11         54  
158              
159 2         16 $self->_finalize($result);
160             }
161              
162 1     1 0 3 sub every {&all}
163              
164             sub all {
165 4     4 0 5 my $self = shift;
166 4         9 my ($list, $iterator, $context) = $self->_prepare(@_);
167              
168 4         10 foreach (@$list) {
169 8 100       34 return 0 unless $iterator->($_);
170             }
171              
172 3         16 return 1;
173             }
174              
175 1     1 0 5 sub some {&any}
176              
177             sub any {
178 6     6 0 10 my $self = shift;
179 6         21 my ($list, $iterator, $context) = $self->_prepare(@_);
180              
181 6 100       20 return 0 unless @$list;
182              
183 5         12 foreach (@$list) {
184 14 100       84 return 1 if $iterator ? $iterator->($_) : $_;
    100          
185             }
186              
187 2         13 return 0;
188             }
189              
190             sub invoke {
191 2     2 0 4 my $self = shift;
192 2         7 my ($list, $method, @args) = $self->_prepare(@_);
193              
194 2         4 my $result = [];
195              
196 2         7 foreach (@$list) {
197 4 50       34 push @$result,
198             [ref $method eq 'CODE' ? $method->(@$_) : $self->$method(@$_)];
199             }
200              
201 2         32 return $result;
202             }
203              
204             sub pluck {
205 4     4 0 8 my $self = shift;
206 4         15 my ($list, $key) = $self->_prepare(@_);
207              
208 4         10 my $result = [];
209              
210 4         9 foreach (@$list) {
211 10         23 push @$result, $_->{$key};
212             }
213              
214 4         12 return $self->_finalize($result);
215             }
216              
217             sub _minmax {
218 2     2   3 my $self = shift;
219 2         6 my ($list, $iterator, $context, $behaviour) = $self->_prepare(@_);
220              
221 6         32 my $computed_list = [map {
222 2         5 { original => $_, computed => $iterator->($_, $context) }
223             } @$list];
224              
225             return _->reduce(
226             $computed_list
227             , sub {
228 6     6   10 my ($memo, $e) = @_;
229 6         11 return $behaviour->($memo, $e);
230             }
231 2         25 , $computed_list->[0]
232             )->{original};
233             }
234              
235             sub max {
236 2     2 0 5 my $self = shift;
237 2         8 my ($list, $iterator, $context) = $self->_prepare(@_);
238              
239 2 100       22 return List::Util::max(@$list) unless defined $iterator;
240              
241             return _->_minmax($list, $iterator, $context, sub {
242 3     3   6 my ($max, $e) = @_;
243 3 50       16 return ($e->{computed} > $max->{computed}) ? $e: $max;
244 1         5 });
245             }
246              
247             sub min {
248 2     2 0 4 my $self = shift;
249 2         46 my ($list, $iterator, $context) = $self->_prepare(@_);
250              
251 2 100       9 return List::Util::min(@$list) unless defined $iterator;
252              
253             return _->_minmax($list, $iterator, $context, sub {
254 3     3   4 my ($min, $e) = @_;
255 3 100       12 return ($e->{computed} < $min->{computed}) ? $e: $min;
256 1         4 });
257             }
258              
259             sub sort : method {
260 2     2 0 4 my $self = shift;
261 2         7 my ($list) = $self->_prepare(@_);
262              
263 2         19 return $self->_finalize([sort @$list]);
264             }
265              
266 3     3 0 7 sub sortBy {&sort_by}
267              
268             sub sort_by {
269 3     3 0 14 my $self = shift;
270 3         8 my ($list, $iterator, $context, $comparator) = $self->_prepare(@_);
271              
272 3 100   5   40 my $cmp = defined $comparator ? $comparator : sub { my ($x, $y) = @_; $x <=> $y } ;
  5         29  
  5         12  
273              
274 3         14 my $result = [sort { $cmp->($iterator->($a, $context), $iterator->($b, $context)) } @$list];
  8         36  
275              
276 3         17 return $self->_finalize($result);
277             }
278              
279             sub reverse : method {
280 1     1 0 7 my $self = shift;
281 1         4 my ($list) = $self->_prepare(@_);
282              
283 1         3 my $result = [reverse @$list];
284              
285 1         4 return $self->_finalize($result);
286             }
287              
288             sub concat {
289 1     1 0 2 my $self = shift;
290 1         3 my ($list, $other) = $self->_prepare(@_);
291              
292 1         3 my $result = [@$list, @$other];
293              
294 1         3 return $self->_finalize($result);
295             }
296              
297             sub unshift : method {
298 1     1 0 2 my $self = shift;
299 1         2 my ($list, @elements) = $self->_prepare(@_);
300              
301 1         2 unshift @$list, @elements;
302 1         2 my $result = $list;
303              
304 1         2 return $self->_finalize($result);
305             }
306              
307             sub pop : method {
308 1     1 0 2 my $self = shift;
309 1         2 my ($list) = $self->_prepare(@_);
310              
311 1         2 pop @$list;
312 1         2 my $result = $list;
313              
314 1         3 return $self->_finalize($result);
315             }
316              
317             sub _partition {
318 3     3   5 my $self = shift;
319 3         9 my ($list, $iterator, $behaviour) = $self->_prepare(@_);
320              
321 3         7 my $result = {};
322 3         5 foreach (@{$list}) {
  3         8  
323 16         43 my $group = $iterator->($_);
324 16         84 $behaviour->($result, $group, $_);
325             }
326 3         11 return $self->_finalize($result);
327             }
328              
329 1     1 0 3 sub groupBy {&group_by}
330              
331             sub group_by {
332 1     1 0 2 my $self = shift;
333             return $self->_partition(@_, sub {
334 6     6   7 my ($result, $group, $o) = @_;
335 6 100       18 if (exists $result->{$group}) {
336 4         3 push @{$result->{$group}}, $o;
  4         10  
337             }
338             else {
339 2         8 $result->{$group} = [$o];
340             }
341 1         8 });
342             }
343              
344 1     1 0 4 sub countBy {&count_by}
345              
346             sub count_by {
347 2     2 0 5 my $self = shift;
348             return $self->_partition(@_, sub {
349 10     10   14 my ($result, $group, $o) = @_;
350 10 100       22 if (exists $result->{$group}) {
351 6         21 $result->{$group} = $result->{$group} + 1;
352             }
353             else {
354 4         12 $result->{$group} = 1;
355             }
356 2         21 });
357             }
358              
359 1     1 0 4 sub sortedIndex {&sorted_index}
360              
361             sub sorted_index {
362 1     1 0 5 my $self = shift;
363 1         4 my ($list, $value, $iterator) = $self->_prepare(@_);
364              
365             # TODO $iterator
366              
367 1         3 my $min = 0;
368 1         3 my $max = @$list;
369 1         2 my $mid;
370              
371 1   33     3 do {
372 1         4 $mid = int(($min + $max) / 2);
373 1 50       6 if ($value > $list->[$mid]) {
374 1         11 $min = $mid + 1;
375             }
376             else {
377 0         0 $max = $mid - 1;
378             }
379             } while ($list->[$mid] == $value || $min > $max);
380              
381 1 50       5 if ($list->[$mid] == $value) {
382 0         0 return $mid;
383             }
384              
385 1         5 return $mid + 1;
386             }
387              
388 4     4 0 9 sub toArray {&to_array}
389              
390             sub to_array {
391 4     4 0 8 my $self = shift;
392 4         18 my ($list) = $self->_prepare(@_);
393              
394 4 100       19 return [values %$list] if ref $list eq 'HASH';
395              
396 3 100       18 return [$list] unless ref $list eq 'ARRAY';
397              
398 2         16 return [@$list];
399             }
400              
401             sub size {
402 1     1 0 3 my $self = shift;
403 1         4 my ($list) = $self->_prepare(@_);
404              
405 1 50       7 return scalar @$list if ref $list eq 'ARRAY';
406              
407 1 50       10 return scalar keys %$list if ref $list eq 'HASH';
408              
409 0         0 return 1;
410             }
411              
412 1     1 0 3 sub head {&first}
413 6     6 0 10 sub take {&first}
414              
415             sub first {
416 13     13 0 22 my $self = shift;
417 13         34 my ($array, $n) = $self->_prepare(@_);
418              
419 13 100       50 return $array->[0] unless defined $n;
420              
421 8         16 return [@{$array}[0 .. $n - 1]];
  8         55  
422             }
423              
424             sub initial {
425 5     5 0 7 my $self = shift;
426 5         14 my ($array, $n) = $self->_prepare(@_);
427              
428 5 100       15 $n = scalar @$array - 1 unless defined $n;
429            
430 5         15 return $self->take($array, $n);
431             }
432              
433 1     1 0 3 sub tail {&rest}
434              
435             sub rest {
436 4     4 0 5 my $self = shift;
437 4         10 my ($array, $index) = $self->_prepare(@_);
438              
439 4 100       26 $index = 1 unless defined $index;
440              
441 4         10 return [@{$array}[$index .. $#$array]];
  4         28  
442             }
443              
444             sub last {
445 2     2 0 3 my $self = shift;
446 2         6 my ($array) = $self->_prepare(@_);
447              
448 2         8 return $array->[-1];
449             }
450              
451             sub shuffle {
452 1     1 0 4 my $self = shift;
453 1         2 my ($array) = $self->_prepare(@_);
454              
455 1         74 return [List::Util::shuffle @$array];
456             }
457              
458             sub compact {
459 2     2 0 4 my $self = shift;
460 2         4 my ($array) = $self->_prepare(@_);
461              
462 2         3 my $new_array = [];
463 2         3 foreach (@$array) {
464 12 100       90 push @$new_array, $_ if $_;
465             }
466              
467 2         8 return $new_array;
468             }
469              
470             sub flatten {
471 10     10 0 14 my $self = shift;
472 10         24 my ($array) = $self->_prepare(@_);
473              
474 10         16 my $cb;
475             $cb = sub {
476 25     25   30 my $result = [];
477 25         32 foreach (@{$_[0]}) {
  25         41  
478 172 100       246 if (ref $_ eq 'ARRAY') {
479 15         18 push @$result, @{$cb->($_)};
  15         54  
480             }
481             else {
482 157         219 push @$result, $_;
483             }
484             }
485 25         55 return $result;
486 10         37 };
487              
488 10         19 my $result = $cb->($array);
489              
490 10         27 return $self->_finalize($result);
491             }
492              
493             sub without {
494 4     4 0 7 my $self = shift;
495 4         9 my ($array, @values) = $self->_prepare(@_);
496              
497             # Nice hack comparing hashes
498              
499 4         5 my $new_array = [];
500 4         9 foreach my $el (@$array) {
501             push @$new_array, $el
502 18 100   30   56 unless defined List::Util::first { $el eq $_ } @values;
  30         91  
503             }
504              
505 4         21 return $new_array;
506             }
507              
508 1     1 0 3 sub unique {&uniq}
509              
510             sub uniq {
511 5     5 0 10 my $self = shift;
512 5         16 my ($array, $is_sorted) = $self->_prepare(@_);
513              
514 5 100       227 return [List::MoreUtils::uniq(@$array)] unless $is_sorted;
515              
516             # We can push first value to prevent unneeded -1 check
517 1         3 my $new_array = [shift @$array];
518 1         3 foreach (@$array) {
519 5 100       17 push @$new_array, $_ unless $_ eq $new_array->[-1];
520             }
521              
522 1         7 return $new_array;
523             }
524              
525             sub intersection {
526 3     3 0 5 my $self = shift;
527 3         8 my (@arrays) = $self->_prepare(@_);
528              
529 3         7 my $seen = {};
530 3         6 foreach my $array (@arrays) {
531 6         35 $seen->{$_}++ for @$array;
532             }
533              
534 3         7 my $intersection = [];
535 3         14 foreach (keys %$seen) {
536 12 100       33 push @$intersection, $_ if $seen->{$_} == @arrays;
537             }
538 3         23 return $intersection;
539             }
540              
541             sub union {
542 1     1 0 3 my $self = shift;
543 1         4 my (@arrays) = $self->_prepare(@_);
544              
545 1         3 my $seen = {};
546 1         4 foreach my $array (@arrays) {
547 3         18 $seen->{$_}++ for @$array;
548             }
549              
550 1         7 return [keys %$seen];
551             }
552              
553             sub difference {
554 1     1 0 2 my $self = shift;
555 1         5 my ($array, $other) = $self->_prepare(@_);
556              
557 1         3 my $new_array = [];
558 1         5 foreach my $el (@$array) {
559 3 100   7   17 push @$new_array, $el unless List::Util::first { $el eq $_ } @$other;
  7         30  
560             }
561              
562 1         4 return $new_array;
563             }
564              
565             sub object {
566 2     2 0 4 my $self = shift;
567 2         6 my (@arrays) = $self->_prepare(@_);
568              
569 2         3 my $object = {};
570 2         4 my $arrays_length = scalar @arrays;
571 2 100       10 if ($arrays_length == 2) {
    50          
572 1         3 my ($keys, $values) = @arrays;
573 1         4 foreach my $i (0..scalar @$keys - 1) {
574 3         4 my $key = $keys->[$i];
575 3         6 my $value = $values->[$i];
576 3         9 $object->{$key} = $value;
577             }
578             } elsif ($arrays_length == 1) {
579             _->reduce($arrays[0]
580             , sub {
581 3     3   5 my ($o, $pair) = @_;
582 3         7 $o->{$pair->[0]} = $pair->[1];
583 3         8 return $o;
584             }
585 1         5 , $object
586             );
587             }
588 2         16 return $object;
589             }
590              
591             sub pairs {
592 1     1 0 3 my $self = shift;
593 1         3 my ($hash) = $self->_prepare(@_);
594              
595 1         3 return [map { [ $_ => $hash->{$_} ] } keys %$hash ];
  2         10  
596             }
597              
598             sub pick {
599 3     3 0 3 my $self = shift;
600 3         6 my ($hash, @picks) = $self->_prepare(@_);
601              
602             return _->reduce(
603             _->flatten(\@picks)
604             , sub {
605 6     6   7 my ($o, $pick) = @_;
606 6         11 $o->{$pick} = $hash->{$pick};
607 6         10 return $o;
608             }
609             , {}
610 3         7 );
611             }
612              
613             sub omit {
614 4     4 0 7 my $self = shift;
615 4         11 my ($hash, @omits) = $self->_prepare(@_);
616              
617 4         6 my %omit_these = map { $_ => $_ } @{_->flatten(\@omits)};
  7         26  
  4         9  
618             return _->reduce(
619             [keys %$hash]
620             , sub {
621 12     12   14 my ($o, $key) = @_;
622 12 100       29 $o->{$key} = $hash->{$key} unless exists $omit_these{$key};
623 12         23 return $o;
624             }
625             , {}
626 4         32 );
627             }
628              
629             sub zip {
630 1     1 0 3 my $self = shift;
631 1         3 my (@arrays) = $self->_prepare(@_);
632              
633             # This code is from List::MoreUtils
634             # (can't use it here directly because of the prototype!)
635 1         2 my $max = -1;
636 1   66     11 $max < $#$_ && ($max = $#$_) foreach @arrays;
637             return [
638 3         4 map {
639 1         4 my $ix = $_;
640 3         15 map $_->[$ix], @_;
641             } 0 .. $max
642             ];
643             }
644              
645 7     7 0 14 sub indexOf {&index_of}
646              
647             sub index_of {
648 7     7 0 10 my $self = shift;
649 7         58 my ($array, $value, $is_sorted) = $self->_prepare(@_);
650              
651 7 100       24 return -1 unless defined $array;
652              
653 5     15   53 return List::MoreUtils::first_index { $_ eq $value } @$array;
  15         42  
654             }
655              
656 3     3 0 7 sub lastIndexOf {&last_index_of}
657              
658             sub last_index_of {
659 3     3 0 6 my $self = shift;
660 3         8 my ($array, $value, $is_sorted) = $self->_prepare(@_);
661              
662 3 50       10 return -1 unless defined $array;
663              
664 3     9   17 return List::MoreUtils::last_index { $_ eq $value } @$array;
  9         26  
665             }
666              
667             sub range {
668 8     8 0 12 my $self = shift;
669 8 100       29 my ($start, $stop, $step) =
    100          
670             @_ == 3 ? @_ : @_ == 2 ? @_ : (undef, @_, undef);
671              
672 8 100       28 return [] unless $stop;
673              
674 7 100       13 $start = 0 unless defined $start;
675              
676 7 100       34 return [$start .. $stop - 1] unless defined $step;
677              
678             my $test = ($start < $stop)
679 6     6   19 ? sub { $start < $stop }
680 4 100   15   21 : sub { $start > $stop };
  15         30  
681              
682 4         9 my $new_array = [];
683 4         8 while ($test->()) {
684 17         26 push @$new_array, $start;
685 17         29 $start += $step;
686             }
687 4         39 return $new_array;
688             }
689              
690             sub mixin {
691 2     2 0 4 my $self = shift;
692 2         6 my (%functions) = $self->_prepare(@_);
693              
694 7     7   76 no strict 'refs';
  7         15  
  7         266  
695 7     7   35 no warnings 'redefine';
  7         11  
  7         16473  
696 2         7 foreach my $name (keys %functions) {
697 2         30 *{__PACKAGE__ . '::' . $name} = sub {
698 2     2   3 my $self = shift;
699              
700 1         16 unshift @_, @{$self->{args}}
  2         8  
701 2 100 66     8 if defined $self->{args} && @{$self->{args}};
702 2         7 $functions{$name}->(@_);
703 2         7 };
704             }
705             }
706              
707 100     100 0 130 sub uniqueId {&unique_id}
708              
709             sub unique_id {
710 100     100 0 106 my $self = shift;
711 100         165 my ($prefix) = $self->_prepare(@_);
712              
713 100 50       171 $prefix = '' unless defined $prefix;
714              
715 100         422 return $prefix . ($UNIQUE_ID++);
716             }
717              
718             sub result {
719 2     2 0 3 my $self = shift;
720 2         5 my ($hash, $key, @args) = $self->_prepare(@_);
721              
722 2         4 my $value = $hash->{$key};
723 2 100       10 return ref $value eq 'CODE' ? $value->(@args) : $value;
724             }
725              
726             sub times {
727 2     2 0 3 my $self = shift;
728 2         5 my ($n, $iterator) = $self->_prepare(@_);
729              
730 2         5 for (0 .. $n - 1) {
731 6         20 $iterator->($_);
732             }
733             }
734              
735             sub after {
736 3     3 0 4 my $self = shift;
737 3         5 my ($n, $func, @args) = $self->_prepare(@_);
738              
739 3         5 my $invocation_count = 0;
740             return sub {
741 19 100   19   84 return ++$invocation_count >= $n ? $func->(@args) : undef;
742 3         12 };
743             }
744              
745             sub template_settings {
746 3     3 0 23 my $self = shift;
747 3         6 my (%args) = @_;
748              
749 3         5 for (qw/interpolate evaluate/) {
750 6 100       16 if (my $value = $args{$_}) {
751 5         16 $self->{template_settings}->{$_} = $value;
752             }
753             }
754             }
755              
756             sub template {
757 14     14 0 6112 my $self = shift;
758 14         29 my ($template) = $self->_prepare(@_);
759              
760 14         22 my $evaluate = $self->{template_settings}->{evaluate};
761 14         18 my $interpolate = $self->{template_settings}->{interpolate};
762              
763             return sub {
764 14     14   76 my ($args) = @_;
765              
766 14         15 my $code = q!sub {my ($args) = @_; my $_t = '';!;
767 14         31 foreach my $arg (keys %$args) {
768 10         33 $code .= "my \$$arg = \$args->{$arg};";
769             }
770              
771 14         97 $template =~ s{$interpolate}{\}; \$_t .= $1; \$_t .= q\{}g;
772 14         87 $template =~ s{$evaluate}{\}; $1; \$_t .= q\{}g;
773              
774 14         19 $code .= '$_t .= q{';
775 14         19 $code .= $template;
776 14         12 $code .= '};';
777 14         19 $code .= 'return $_t};';
778              
779 14         1456 my $sub = eval $code;
780              
781 14         307 return $sub->($args);
782 14         61 };
783             }
784              
785             our $ONCE;
786              
787             sub once {
788 1     1 0 3 my $self = shift;
789 1         3 my ($func) = @_;
790              
791             return sub {
792 2 100   2   16 return if $ONCE;
793              
794 1         2 $ONCE++;
795 1         4 $func->(@_);
796 1         6 };
797             }
798              
799             sub wrap {
800 2     2 0 3 my $self = shift;
801 2         5 my ($function, $wrapper) = $self->_prepare(@_);
802              
803             return sub {
804 2     2   21 $wrapper->($function, @_);
805 2         10 };
806             }
807              
808             sub compose {
809 2     2 0 4 my $self = shift;
810 2         4 my (@functions) = @_;
811              
812             return sub {
813 2     2   17 my @args = @_;
814 2         5 foreach (reverse @functions) {
815 4         20 @args = $_->(@args);
816             }
817              
818 2 50       19 return wantarray ? @args : $args[0];
819 2         11 };
820             }
821              
822             sub bind {
823 8     8 0 10 my $self = shift;
824 8         20 my ($function, $object, @args) = $self->_prepare(@_);
825              
826             return sub {
827 8     8   55 $function->($object, @args, @_);
828 8         38 };
829             }
830              
831             sub keys : method {
832 5     5 0 6 my $self = shift;
833 5         16 my ($object) = $self->_prepare(@_);
834              
835 5 100 100     48 die 'Not a hash reference' unless ref $object && ref $object eq 'HASH';
836              
837 1         17 return [keys %$object];
838             }
839              
840             sub values {
841 1     1 0 10 my $self = shift;
842 1         4 my ($object) = $self->_prepare(@_);
843              
844 1 50 33     8 die 'Not a hash reference' unless ref $object && ref $object eq 'HASH';
845              
846 1         16 return [values %$object];
847             }
848              
849             sub functions {
850 1     1 0 2 my $self = shift;
851 1         5 my ($object) = $self->_prepare(@_);
852              
853 1 50 33     11 die 'Not a hash reference' unless ref $object && ref $object eq 'HASH';
854              
855 1         4 my $functions = [];
856 1         4 foreach (keys %$object) {
857 4 100 100     36 push @$functions, $_
858             if ref $object->{$_} && ref $object->{$_} eq 'CODE';
859             }
860 1         4 return $functions;
861             }
862              
863             sub extend {
864 6     6 0 12 my $self = shift;
865 6         21 my ($destination, @sources) = $self->_prepare(@_);
866              
867 6         15 foreach my $source (@sources) {
868 8         26 foreach my $key (keys %$source) {
869 10 100       24 next unless defined $source->{$key};
870 9         35 $destination->{$key} = $source->{$key};
871             }
872             }
873              
874 6         52 return $destination;
875             }
876              
877             sub defaults {
878 2     2 0 4 my $self = shift;
879 2         9 my ($object, @defaults) = $self->_prepare(@_);
880              
881 2         6 foreach my $default (@defaults) {
882 4         12 foreach my $key (keys %$default) {
883 6 100       30 next if exists $object->{$key};
884 2         6 $object->{$key} = $default->{$key};
885             }
886             }
887              
888 2         8 return $object;
889             }
890              
891             sub clone {
892 1     1 0 2 my $self = shift;
893 1         4 my ($object) = $self->_prepare(@_);
894              
895             # Scalars will be copied, everything deeper not
896 1         2 my $cloned = {};
897 1         4 foreach my $key (keys %$object) {
898 2         5 $cloned->{$key} = $object->{$key};
899             }
900              
901 1         3 return $cloned;
902             }
903              
904 2     2 0 5 sub isEqual {&is_equal}
905              
906             sub is_equal {
907 2     2 0 3 my $self = shift;
908 2         6 my ($object, $other) = $self->_prepare(@_);
909             }
910              
911 9     9 0 15 sub isEmpty {&is_empty}
912              
913             sub is_empty {
914 9     9 0 12 my $self = shift;
915 9         19 my ($object) = $self->_prepare(@_);
916              
917 9 100       27 return 1 unless defined $object;
918              
919 7 100       34 if (!ref $object) {
    100          
    100          
    50          
920 2 100       9 return 1 if $object eq '';
921             }
922             elsif (ref $object eq 'HASH') {
923 2 100       12 return 1 if !(keys %$object);
924             }
925             elsif (ref $object eq 'ARRAY') {
926 2 100       9 return 1 if @$object == 0;
927             }
928             elsif (ref $object eq 'Regexp') {
929 1 50       10 return 1 if $object eq qr//;
930             }
931              
932 3         13 return 0;
933             }
934              
935 4     4 0 11 sub isArray {&is_array}
936              
937             sub is_array {
938 4     4 0 16 my $self = shift;
939 4         12 my ($object) = $self->_prepare(@_);
940              
941 4 50 66     46 return 1 if defined $object && ref $object && ref $object eq 'ARRAY';
      66        
942              
943 1         7 return 0;
944             }
945              
946 2     2 0 4 sub isString {&is_string}
947              
948             sub is_string {
949 2     2 0 66 my $self = shift;
950 2         7 my ($object) = $self->_prepare(@_);
951              
952 2 50 33     14 return 0 unless defined $object && !ref $object;
953              
954 2 100       7 return 0 if $self->is_number($object);
955              
956 1         4 return 1;
957             }
958              
959 3     3 0 4 sub isNumber {&is_number}
960              
961             sub is_number {
962 5     5 0 7 my $self = shift;
963 5         10 my ($object) = $self->_prepare(@_);
964              
965 5 100 66     24 return 0 unless defined $object && !ref $object;
966              
967             # From JSON::PP
968 4         44 my $flags = B::svref_2object(\$object)->FLAGS;
969 4 50       41 my $is_number = $flags & (B::SVp_IOK | B::SVp_NOK)
    100          
970             and !($flags & B::SVp_POK) ? 1 : 0;
971              
972 4 100       14 return 1 if $is_number;
973              
974 2         7 return 0;
975             }
976              
977 3     3 0 27 sub isFunction {&is_function}
978              
979             sub is_function {
980 3     3 0 3 my $self = shift;
981 3         8 my ($object) = $self->_prepare(@_);
982              
983 3 100 66     30 return 1 if defined $object && ref $object && ref $object eq 'CODE';
      100        
984              
985 2         7 return 0;
986             }
987              
988 2     2 0 5 sub isRegExp {&is_regexp}
989              
990             sub is_regexp {
991 2     2 0 3 my $self = shift;
992 2         6 my ($object) = $self->_prepare(@_);
993              
994 2 100 33     32 return 1 if defined $object && ref $object && ref $object eq 'Regexp';
      66        
995              
996 1         5 return 0;
997             }
998              
999 5     5 0 23 sub isUndefined {&is_undefined}
1000              
1001             sub is_undefined {
1002 5     5 0 7 my $self = shift;
1003 5         12 my ($object) = $self->_prepare(@_);
1004              
1005 5 100       21 return 1 unless defined $object;
1006              
1007 3         15 return 0;
1008             }
1009              
1010 7     7 0 14 sub isBoolean {&is_boolean}
1011              
1012             sub is_boolean {
1013 7     7 0 8 my $self = shift;
1014 7         12 my ($object) = @_;
1015              
1016 7 50 66     65 return 1
      66        
1017             if Scalar::Util::blessed($object)
1018             && ( $object->isa('UnderscoreJS::_True')
1019             || $object->isa('UnderscoreJS::_False'));
1020              
1021 5         23 return 0;
1022             }
1023              
1024             sub chain {
1025 5     5 0 6 my $self = shift;
1026              
1027 5         7 $self->{chain} = 1;
1028              
1029 5         26 return $self;
1030             }
1031              
1032             sub value {
1033 7     7 0 9 my $self = shift;
1034              
1035 7 100       25 return wantarray ? @{$self->{args}} : $self->{args}->[0];
  1         7  
1036             }
1037              
1038             sub _prepare {
1039 350     350   373 my $self = shift;
1040 350 100 66     946 unshift @_, @{$self->{args}} if defined $self->{args} && @{$self->{args}};
  30         57  
  350         1235  
1041 350         767 return @_;
1042             }
1043              
1044             sub _finalize {
1045 62     62   81 my $self = shift;
1046              
1047             return
1048 62 100       337 $self->{chain} ? do { $self->{args} = [@_]; $self }
  15 100       31  
  15         109  
1049             : wantarray ? @_
1050             : $_[0];
1051             }
1052              
1053             package UnderscoreJS::_True;
1054              
1055 7     7   143 use overload '""' => sub {'true'}, fallback => 1;
  7     2   13  
  7         93  
  2         2513  
1056 7     7   630 use overload 'bool' => sub {1}, fallback => 1;
  7     2   12  
  7         38  
  2         6  
1057 7 0   7   486 use overload 'eq' => sub { $_[1] eq 'true' ? 1 : 0; }, fallback => 1;
  7     0   10  
  7         52  
  0         0  
1058 7 0   7   509 use overload '==' => sub { $_[1] == 1 ? 1 : 0; }, fallback => 1;
  7     0   10  
  7         38  
  0         0  
1059              
1060 8     8   42 sub new { bless {}, $_[0] }
1061              
1062             package UnderscoreJS::_False;
1063              
1064 7     7   694 use overload '""' => sub {'false'}, fallback => 1;
  7     0   19  
  7         40  
  0         0  
1065 7     7   483 use overload 'bool' => sub {0}, fallback => 1;
  7     4   12  
  7         41  
  4         9  
1066 7 50   7   506 use overload 'eq' => sub { $_[1] eq 'false' ? 1 : 0; }, fallback => 1;
  7     2   13  
  7         37  
  2         152  
1067 7 0   7   506 use overload '==' => sub { $_[1] == 0 ? 1 : 0; }, fallback => 1;
  7     0   17  
  7         37  
  0         0  
1068              
1069 6     6   46 sub new { bless {}, $_[0] }
1070              
1071             1;
1072             __END__