File Coverage

blib/lib/DBIx/MoCo/List.pm
Criterion Covered Total %
statement 71 173 41.0
branch 20 68 29.4
condition 7 20 35.0
subroutine 21 54 38.8
pod 32 36 88.8
total 151 351 43.0


line stmt bran cond sub pod time code
1             package DBIx::MoCo::List;
2 15     15   89602 use strict;
  15         28  
  15         599  
3 15     15   75 use warnings;
  15         30  
  15         579  
4 15     15   109 use Carp qw/croak/;
  15         28  
  15         863  
5 15     15   92 use List::Util ();
  15         27  
  15         251  
6 15     15   14743 use List::MoreUtils ();
  15         18191  
  15         1661  
7              
8             our $AUTOLOAD;
9              
10             sub AUTOLOAD {
11 2     2   1020 my $self = $_[0];
12 2   33     9 my $class = ref($self) || $self;
13 2 50       7 $self = undef unless ref($self);
14 2         15 (my $method = $AUTOLOAD) =~ s!.+::!!;
15 2 50       8 return if $method eq 'DESTROY';
16 15     15   100 no strict 'refs';
  15         26  
  15         43173  
17 2 50       13 if ($method =~ /^map_(.+)$/o) {
18 2         11 *$AUTOLOAD = $class->_map_handler($1);
19 2         100 goto &$AUTOLOAD;
20             }
21             }
22              
23             sub _map_handler {
24 2     2   4 my $class = shift;
25 2         6 my $method = shift;
26             return sub {
27 2     2   15 shift->map(sub { $_->$method() });
  8         55  
28 2         18 };
29             }
30              
31             sub new {
32 43     43 0 886 my ($class, $array) = @_;
33 43   66     212 $class = ref $class || $class;
34 43   50     149 $array ||= [];
35 43 50       170 croak sprintf("Argument must be an array reference (%s)", ref $array)
36             unless ref $array eq 'ARRAY';
37 43         398 bless $array, $class;
38             }
39              
40             sub push {
41 0     0 1 0 my $self = shift;
42 0         0 push @$self, @_;
43 0         0 $self;
44             }
45              
46             sub unshift {
47 0     0 1 0 my $self = shift;
48 0         0 unshift @$self, @_;
49 0         0 $self;
50             }
51              
52             sub shift {
53 0     0 1 0 shift @{$_[0]};
  0         0  
54             }
55              
56             sub pop {
57 1     1 1 6 pop @{$_[0]};
  1         4  
58             }
59              
60             sub first {
61 5     5 1 1652 $_[0]->[0];
62             }
63              
64             sub last {
65 1     1 1 5 $_[0]->[-1];
66             }
67              
68             sub slice {
69 12     12 1 20 my $self = CORE::shift;
70 12         30 my ($s, $e) = @_;
71 12         22 my $last = $#{$self};
  12         31  
72             # warn "s: $s, e: $e, last: $last";
73 12 100 33     50 if (defined $e) {
    50 33        
74 10 50 33     54 if ($s == 0 && $last <= $e) {
75 10         72 return $self;
76             } else {
77 0 0       0 $e = $last if ($last < $e);
78 0         0 return $self->new([ @$self[ $s .. $e ] ]);
79             }
80             } elsif (defined $s && 0 < $s && $last <= $s) {
81             # warn $self->first . "s: $s, e: $e, self:" . $#{$self};
82 0         0 return $self->new([]);
83             } else {
84 2         27 return $self;
85             }
86             }
87              
88             sub dump {
89 0     0 1 0 my $self = CORE::shift;
90 0         0 require Data::Dumper;
91 0         0 Data::Dumper->new([ $self->to_a ])->Purity(1)->Terse(1)->Dump;
92             }
93              
94             sub zip {
95 0     0 1 0 my $self = CORE::shift;
96 0         0 my $array = \@_;
97 0         0 my $index = 0;
98             $self->collect(sub {
99 0     0   0 my $ary = $self->new([$_]);
100 0         0 $ary->push($_->[$index]) for @$array;
101 0         0 $index++;
102 0         0 $ary;
103 0         0 });
104             }
105              
106             sub delete {
107 0     0 1 0 my ($self, $value, $code) = @_;
108 0         0 my $found = 0;
109 0 0       0 do { my $item = $self->shift; $item == $value ? $found = 1 : $self->push($item) } for (0..$self->_last_index);
  0         0  
  0         0  
110             $found ? $value
111 0 0       0 : ref $code eq 'CODE' ? do { local $_ = $value; return $code->($_) }
  0 0       0  
  0         0  
112             : return ;
113             }
114              
115             sub delete_at {
116 0     0 1 0 my ($self, $pos) = @_;
117 0         0 my $last_index = $self->_last_index;
118 0 0       0 return if $pos > $last_index ;
119 0         0 my $result;
120             $_ == $pos ? $result = $self->shift
121 0 0       0 : $self->push($self->shift) for 0..$last_index;
122 0         0 return $result;
123             }
124              
125             sub delete_if {
126 0     0 1 0 my ($self, $code) = @_;
127 0 0       0 croak "Argument must be a code" unless ref $code eq 'CODE';
128 0         0 my $last_index = $self->_last_index;
129 0         0 for (0..$last_index) {
130 0         0 my $item = $self->shift;
131 0         0 local $_ = $item;
132 0 0       0 $self->push($item) if $code->($_);
133             }
134 0         0 return $self;
135             }
136              
137             sub inject {
138 0     0 1 0 my ($self, $result, $code) = @_;
139 0 0       0 croak "Argument must be a code" unless ref $code eq 'CODE';
140 0         0 $result = $code->($result, $_) for @{$self->dup};
  0         0  
141 0         0 return $result;
142             }
143              
144             sub join {
145 0     0 1 0 my ($self, $delimiter) = @_;
146 0         0 join $delimiter, @$self;
147             }
148              
149             sub each_index {
150 0     0 1 0 my ($self, $code) = @_;
151 0         0 $self->new([ 0..$self->_last_index ])->each($code);
152             }
153              
154             sub _last_index {
155 0     0   0 my $self = CORE::shift;
156 0 0       0 $self->length ? $self->length - 1 : 0;
157             };
158              
159             sub concat {
160 0     0 1 0 my ($self, $array) = @_;
161 0         0 $self->push(@$array);
162 0         0 $self;
163             }
164              
165             *append = \&concat;
166              
167             sub prepend {
168 0     0 1 0 my ($self, $array) = @_;
169 0         0 $self->unshift(@$array);
170 0         0 $self;
171             }
172              
173             sub _append_undestructive {
174 0     0   0 my ($self, $array) = @_;
175 0         0 $self->dup->push(@$array);
176             }
177              
178             sub _prepend_undestructive {
179 0     0   0 my ($self, $array) = @_;
180 0         0 $self->dup->unshift(@$array);
181             }
182              
183             sub add {
184 0     0 0 0 my ($self, $array, $bool) = @_;
185 0 0       0 $bool ? $self->_prepend_undestructive($array)
186             : $self->_append_undestructive($array);
187             }
188              
189             sub each {
190 0     0 1 0 my ($self, $code) = @_;
191 0 0       0 croak "Argument must be a code" unless ref $code eq 'CODE';
192 0         0 $code->($_) for @{$self->dup};
  0         0  
193 0         0 $self;
194             }
195              
196             sub collect {
197 2     2 1 3 my ($self, $code) = @_;
198 2 50       9 croak "Argument must be a code" unless ref $code eq 'CODE';
199 2         5 my @collected = CORE::map &$code, @{$self->dup};
  2         9  
200 2 100       20 wantarray ? @collected : $self->new(\@collected);
201             }
202              
203             *map = \&collect;
204              
205             sub grep {
206 1     1 1 2 my ($self, $code) = @_;
207 1 50       3 $code or return;
208 1         1 my @grepped;
209 1 50       4 if (!ref($code)) {
    0          
210 1         8 for (@$self) {
211 4 100       25 CORE::push @grepped, $_ if $_->$code;
212             }
213             } elsif (ref $code eq 'CODE') {
214 0         0 @grepped = CORE::grep &$code, @$self;
215             } else {
216 0         0 croak "Invalid code";
217             }
218 1 50       5 wantarray ? @grepped : $self->new(\@grepped);
219             }
220              
221             sub find {
222 0     0 1 0 my ($self, $code) = @_;
223 0 0       0 croak "Argument must be a code" unless ref $code eq 'CODE';
224 0 0       0 for (@$self) { &$code and return $_ }
  0         0  
225             }
226              
227             sub index_of {
228 6     6 1 320 my ($self, $target) = @_;
229 6 100   14   29 my $code = (ref $target eq 'CODE') ? $target : sub { CORE::shift eq $target };
  14         70  
230 6         18 for (my $i = 0; $i < $self->length; $i++) {
231 17 100       31 &$code($self->[$i]) and return $i;
232             }
233             }
234              
235             sub sort {
236 0     0 1 0 my ($self, $code) = @_;
237 0 0       0 my @sorted = $code ? CORE::sort { $code->($a, $b) } @$self : CORE::sort @$self;
  0         0  
238 0 0       0 wantarray ? @sorted : $self->new(\@sorted);
239             }
240              
241             sub compact {
242 0     0 1 0 CORE::shift->grep(sub { defined });
  0     0   0  
243             }
244              
245             sub length {
246 41     41 1 17375 scalar @{$_[0]};
  41         203  
247             }
248              
249             *size = \&length;
250              
251             sub flatten {
252 0     0 1 0 my $self = CORE::shift;
253 0     0   0 $self->collect(sub { _flatten($_) });
  0         0  
254             }
255              
256             sub _flatten {
257 0     0   0 my $element = CORE::shift;
258 0         0 (ref $element and ref $element eq 'ARRAY')
259 0 0 0     0 ? CORE::map { _flatten($_) } @$element
260             : $element;
261             }
262              
263             sub is_empty {
264 0     0 1 0 !$_[0]->length;
265             }
266              
267             sub uniq {
268 0     0 1 0 my $self = CORE::shift;
269 0         0 $self->new([ List::MoreUtils::uniq(@$self) ]);
270             }
271              
272             sub reduce {
273 0     0 1 0 my ($self, $code) = @_;
274 0 0       0 croak "Argument must be a code" unless ref $code eq 'CODE';
275 0     0   0 List::Util::reduce { $code->($a, $b) } @$self;
  0         0  
276             }
277              
278             sub to_a {
279 2     2 0 4 my @unblessed = @{$_[0]};
  2         17  
280 2         10 \@unblessed;
281             }
282              
283             sub as_list { # for Template::Iterator
284 0     0 0 0 CORE::shift;
285             }
286              
287             sub dup {
288 2     2 1 7 __PACKAGE__->new($_[0]->to_a);
289             }
290              
291             sub reverse {
292 0     0 1   my $self = CORE::shift;
293 0           $self->new([ reverse @$self ]);
294             }
295              
296             sub sum {
297 0     0 1   List::Util::sum @{$_[0]};
  0            
298             }
299              
300             1;
301              
302             =head1 NAME
303              
304             DBIx::MoCo::List - Array iterator for DBIx::MoCo.
305              
306             =head1 SYNOPSIS
307              
308             my $array_ref = [
309             {name => 'jkondo'},
310             {name => 'cinnamon'}
311             ];
312             my $list = DBIx::MoCo::List->new($array_ref);
313              
314             $list->size; #=> 2
315             my $first = $list->shift; #=> {name => 'jkondo'}
316             $list->push($first); #=> [{name => 'cinnamon'}, {name => 'jkondo'}];
317              
318             # DBIx::MoCo::List provides much more useful methods. For more
319             # details, see the sections below.
320              
321             =head1 METHODS
322              
323             =over 4
324              
325             =item dump ()
326              
327             Dump the content of C<$self> using L.
328              
329             =item push ( I<@array> )
330              
331             =item unshift ( I<@array> )
332              
333             Sets the argument into C<$self>, a refernce to an array blessed by
334             DBIx::MoCo::List, like the same name functions provided by Perl core,
335             then returns a DBIx::MoCo::List object.
336              
337             my $list = DBIx::MoCo::List->new([qw(1 2 3)]);
338             $list->push(4, 5); #=> [1, 2, 3, 4, 5]
339             $list->unshift(0); #=> [0, 1, 2, 3, 4, 5]
340              
341             =item concat ( I<\@array> )
342              
343             =item prepend ( I<\@array> )
344              
345             They're almost the same as C/C described above
346             except that the argument shoud be a reference to an array.
347              
348             my $list = DBIx::MoCo::List->new([1, 2, 3]);
349             $list->concat([4, 5]); #=> [1, 2, 3, 4, 5]
350             $list->prepend([0]); #=> [0, 1, 2, 3, 4, 5]
351              
352             =item shift ()
353              
354             =item pop ()
355              
356             Pulls out the first/last element from C<$self>, a refernce to an array
357             blessed by DBIx::MoCo::List, then returns it like the same name
358             functions in Perl core.
359              
360             $list = DBIx::MoCo::List->new([1, 2, 3]);
361             $list->shift; #=> 1
362             $list->pop; #=> 3
363             $list->dump #=> [2]
364              
365             =item first ()
366              
367             =item last ()
368              
369             Returns the first/last element of C<$self>, a refernce to an array
370             blessed by DBIx::MoCo::List. These methods aren't destructive contrary
371             to C/C method.
372              
373             $list = DBIx::MoCo::List->new([1, 2, 3]);
374             $list->first; #=> 1
375             $list->last; #=> 3
376             $list->dump #=> [1, 2, 3]
377              
378             =item slice ( I<$start>, I<$end> )
379              
380             Returns the elements whose indexes are between C<$start> and C<$end>
381             as a DBIx::MoCo::List object.
382              
383             $list = DBIx::MoCo::List->new([qw(1 2 3 4)]);
384             $list->slice(1, 2) #=> [2, 3]
385              
386             =item zip ( I<\@array1>, I<\@array2>, ... )
387              
388             Bundles up the elements in each arguments into an array or a
389             DBIx::MoCo::List object along with the context.
390              
391             my $list = DBIx::MoCo::List->new([1, 2, 3]);
392             $list->zip([4, 5, 6], [7, 8, 9]);
393             #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
394              
395             # When the numbers of each list are different...
396             $list = DBIx::MoCo::List->new([1, 2, 3]);
397             $list->zip([4, 5], [7, 8, 9]);
398             #=> [[1, 4, 7], [2, 5, 8], [3, undef, 9]]
399              
400             my $list = DBIx::MoCo::List->new([1, 2]);
401             $list->zip([4, 5], [7, 8, 9]);
402             #=> [[1, 4, 7], [2, 5, 8]]
403              
404             =item delete ( I<$value>, I<$code> )
405              
406             Deletes the same values as C<$value> in C<$self>, a refernce to an
407             array blessed by DBIx::MoCo::List, and returns the value if found. If
408             the value is not found in C<$self> and C<$code> is passed in, the code
409             is executed using the value as an argument to find the value to be
410             deleted.
411              
412             $list = DBIx::MoCo::List->new([1, 2, 3, 2, 1]);
413             $list->delete(2); #=> 2
414             $list->dump #=> [1, 3, 1]
415              
416             =item delete_at ( I<$pos> )
417              
418             Deletes the element at C<$pos> and returns it.
419              
420             $list = DBIx::MoCo::List->new([1, 2, 3, 2, 1]);
421             $list->delete_at(3); #=> 2
422             $list->dump #=> [1, 2, 3, 1]
423              
424             =item delete_if ( I<$code> )
425              
426             Deletes the elements if C<$code> returns false value with each element
427             as an argument.
428              
429             $list = DBIx::MoCo::List->new([1, 2, 3, 4]);
430             $list->delete_if(sub { ($_ % 2) == 0) });
431             $list->dump #=> [2, 4]
432              
433             =item inject ( I<$result>, I<$code> )
434              
435             Executes folding calculation using C<$code> through each element and
436             returns the result.
437              
438             $list = DBIx::MoCo::List->new([1, 2, 3, 4]);
439             $list->inject(0, sub { $_[0] + $_[1] }); #=> 10
440              
441             =item join ( I<$delimiter> )
442              
443             Joins all the elements by C<$delimiter>.
444              
445             $list = DBIx::MoCo::List->new([0 1 2 3]);
446             $list->join(', ') #=> '0, 1, 2, 3'
447              
448             =item each_index ( I<$code> )
449              
450             Executes C<$code> with each index of C<$self>, a refernce to an array
451             blessed by DBIx::MoCo::List.
452              
453             $list = DBIx::MoCo::List->new([1, 2, 3]);
454             $list->each_index(sub { do_something($_) });
455              
456             =item each ( I<$code> )
457              
458             Executes C<$code> with each value of C<$self>, a refernce to an array
459             blessed by DBIx::MoCo::List.
460              
461             $list = DBIx::MoCo::List->new([1, 2, 3]);
462             $list->each(sub { do_something($_) });
463              
464             =item collect ( I<$code> )
465              
466             Executes C<$code> with each element of C<$self>, a refernce to an
467             array blessed by DBIx::MoCo::List using CORE::map() and returns the
468             results as a list or DBIx::MoCo:List object along with the context.
469              
470             $list = DBIx::MoCo::List->new([1, 2, 3]);
471             $list->map(sub { $_ * 2 }); #=> [2, 4, 6]
472              
473             =item map ( I<$code> )
474              
475             An alias of C method described above.
476              
477             =item grep ( I<$code> )
478              
479             Executes C<$code> with each element of C<$self>, a refernce to an
480             array blessed by DBIx::MoCo::List using CORE::grep() and returns the
481             results as a list or DBIx::MoCo:List object along with the context.
482              
483             $list = DBIx::MoCo::List->new([qw(1 2 3 4)]);
484             $list->grep(sub { ($_ % 2) == 0 }); #=> [2, 4]
485              
486             =item find ( I<$code> )
487              
488             Returns the first value found in C<$self>, a refernce to an array
489             blessed by DBIx::MoCo::List, as a result of C<$code>..
490              
491             $list = DBIx::MoCo::List->new([1, 2, 3, 4]);
492             $list->find(sub { ($_ % 2) == 0 }); #=> 2
493              
494             =item index_of ( I<$arg> )
495              
496             Returns index of given target or given code returns true.
497              
498             $list = DBIx::MoCo::List->new([qw(foo bar baz)]);
499             $list->index_of('bar'); #=> 1
500             $list->index_of(sub { shift eq 'bar' }); #=> 1
501              
502             =item sort ( I<$code> )
503              
504             Sorts out each element and returns the result as a list or
505             DBIx::MoCo:List object along with the context.
506              
507             $list = DBIx::MoCo::List->new([qw(3 2 4 1]);
508             $list->sort; #=> [1, 2, 3, 4]
509             $list->sort(sub { $_[1] <=> $_[0] }); #=> [4, 3, 2, 1]
510              
511             =item compact ()
512              
513             Eliminates undefined values in C<$self>, a refernce to an array
514             blessed by DBIx::MoCo::List.
515              
516             $list = DBIx::MoCo::List->new([1, 2, undef, 3, undef, 4]);
517             $list->compact; #=> [1, 2, 3, 4]
518              
519             =item length ()
520              
521             Returns the length of C<$self>, a refernce to an array blessed by
522             DBIx::MoCo::List.
523              
524             $list = DBIx::MoCo::List->new([qw(1 2 3 4)]);
525             $list->length; #=> 4
526              
527             =item size ()
528              
529             An alias of C method described above.
530              
531             =item flatten ()
532              
533             Returns a list or DBIx::MoCo::List object which is recursively
534             flattened out.
535              
536             $list = DBIx::MoCo::List->new([1, [2, 3, [4], 5]]);
537             $list->flattern; #=> [1, 2, 3, 4, 5]
538              
539             =item is_empty ()
540              
541             Returns true if C<$self>, a refernce to an array blessed by
542             DBIx::MoCo::List, is empty.
543              
544             =item uniq ()
545              
546             Uniquifies the elements in C<$self>, a refernce to an array blessed by
547             DBIx::MoCo::List, and returns the result.
548              
549             $list = DBIx::MoCo::List->new([1, 2, 2, 3, 3, 4])
550             $list->uniq; #=> [1, 2, 3, 4]
551              
552             =item reduce ( I<$code> )
553              
554             Reduces the list by C<$code>.
555              
556             # finds the maximum value
557             $list = DBIx::MoCo::List->new([4, 1, 3, 2])
558             $list->reduce(sub { $_[0] > $_[1] ? $_[0] : $_[1] }); #=> 4
559              
560             See L to get to know about details of C.
561              
562             =item reverse ()
563              
564             Returns an reversely ordered C<$self>, a refernce to an array blessed
565             by DBIx::MoCo::List.
566              
567             $list = DBIx::MoCo::List->new([4, 1, 3, 2])
568             $list->reverse; #=> [2, 3, 1, 4]
569              
570             =item dup ()
571              
572             Returns a duplicated C<$self>, a refernce to an array blessed by
573             DBIx::MoCo::List.
574              
575             =item sum ()
576              
577             Returns the sum of each element in C<$self>, a refernce to an array
578             blessed by DBIx::MoCo::List.
579              
580             $list = DBIx::MoCo::List->new([1, 2, 3, 4]);
581             $list->sum; #=> 10
582              
583             =back
584              
585             =head1 AUTO-GENERATED METHODS
586              
587             =over 4
588              
589             =item map_XXX ()
590              
591             Returns the results of C method of each elements as a list or a
592             DBIx::MoCo::List object along with the context.
593              
594             my $list = Your::MoCo::Class->retrieve_all;
595             my $names = $list->map_name;
596              
597             In this case, C<$names> is a list of the return values of C
598             method of each element in C<$list>.
599              
600             =back
601              
602             =head1 SEE ALSO
603              
604             L, L, L
605              
606             =head1 AUTHOR
607              
608             Junya Kondo, Ejkondo@hatena.comE,
609             Naoya Ito, Enaoya@hatena.ne.jpE,
610             Kentaro Kuribayashi, Ekentarok@gmail.comE
611              
612             =head1 COPYRIGHT AND LICENSE
613              
614             Copyright (C) Hatena Inc. All Rights Reserved.
615              
616             This library is free software; you may redistribute it and/or modify
617             it under the same terms as Perl itself.
618              
619             =cut