File Coverage

blib/lib/Array/Iterator.pm
Criterion Covered Total %
statement 76 78 97.4
branch 43 44 97.7
condition 4 6 66.6
subroutine 20 21 95.2
pod 8 13 61.5
total 151 162 93.2


line stmt bran cond sub pod time code
1             package Array::Iterator;
2              
3 5     5   144357 use strict;
  5         26  
  5         164  
4 5     5   25 use warnings;
  5         9  
  5         5301  
5              
6             our $VERSION = '0.12'; # VERSION
7              
8             ### constructor
9              
10             sub new {
11 18     18 1 5101 my ($_class, @array) = @_;
12 18 100       58 (@array)
13             || die "Insufficient Arguments : you must provide something to iterate over";
14 17   33     66 my $class = ref($_class) || $_class;
15 17         26 my $_array;
16 17 100       64 if (scalar @array == 1) {
17 8 100       27 if (ref $array[0] eq "ARRAY") {
    100          
18 3         5 $_array = $array[0];
19             }
20             elsif (ref $array[0] eq "HASH") {
21             die "Incorrect type : HASH reference must contain the key __array__"
22 4 100       25 unless exists $array[0]->{__array__};
23             die "Incorrect type : __array__ value must be an ARRAY reference"
24 2 100       13 unless ref $array[0]->{__array__} eq 'ARRAY';
25 1         3 $_array = $array[0]->{__array__};
26             }
27             else {
28 1         9 die "Incorrect Type : the argument must be an array or hash reference";
29             }
30             }
31             else {
32 9         15 $_array = \@array;
33             }
34 13         47 my $iterator = {
35             _current_index => 0,
36             _length => 0,
37             _iteratee => [],
38             _iterated => 0,
39             };
40 13         24 bless($iterator, $class);
41 13         20 $iterator->_init(scalar(@{$_array}), $_array);
  13         45  
42 13         31 return $iterator;
43             }
44              
45             ### methods
46              
47             # private methods
48              
49             sub _init {
50 15     15   574 my ($self, $length, $iteratee) = @_;
51 15 100 100     64 (defined($length) && defined($iteratee))
52             || die "Insufficient Arguments : you must provide an length and an iteratee";
53 13         29 $self->{_current_index} = 0;
54 13         50 $self->{_length} = $length;
55 13         36 $self->{_iteratee} = $iteratee;
56             }
57              
58             # protected method
59              
60             # this can be used in a subclass to access the value
61              
62             # we need to alter this so its an lvalue
63             sub _current_index : lvalue {
64 110 100   110   532 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
65             || die "Illegal Operation : This method can only be called by a subclass";
66             $_[0]->{_current_index}
67 109         860 }
68              
69             # this we should never need to alter
70             # so we dont make it a lvalue
71             sub _iteratee {
72 42 100   42   400 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
73             || die "Illegal Operation : This method can only be called by a subclass";
74             $_[0]->{_iteratee}
75 41         231 }
76              
77             # we move this from a private method
78             # to a protected one, and check our access
79             # as well
80             sub _getItem {
81 136 100   136   724 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
82             || die "Illegal Operation : This method can only be called by a subclass";
83 135         657 my ($self, $iteratee, $index) = @_;
84 135         322 return $iteratee->[$index];
85             }
86              
87 0     0   0 sub _get_item { my $self = shift; $self->_getItem(@_) }
  0         0  
88              
89             # we need to alter this so its an lvalue
90             sub _iterated : lvalue {
91 42 50   42   98 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
92             || die "Illegal Operation : This method can only be called by a subclass";
93             $_[0]->{_iterated}
94 42         235 }
95              
96             # public methods
97              
98             # this defines the interface
99             # an iterator object will have
100              
101             sub iterated {
102 2     2 0 6357 my ($self) = @_;
103 2         9 return $self->{_iterated};
104             }
105              
106             sub has_next {
107 57     57 1 386 my ($self, $n) = @_;
108              
109 57 100       114 if(not defined $n) { $n = 1 }
  49 100       60  
    100          
110 1         7 elsif(not $n) { die "has_next(0) doesn't make sense, did you mean current()?" }
111 1         7 elsif($n < 0) { die "has_next() with negative argument doesn't make sense, perhaps you should use a BiDirectional iterator" }
112              
113 55         88 my $idx = $self->{_current_index} + ($n - 1);
114              
115 55 100       137 return ($idx < $self->{_length}) ? 1 : 0;
116             }
117              
118 47     47 0 6243 sub hasNext { my $self = shift; $self->has_next(@_) }
  47         90  
119              
120             sub next {
121 37     37 1 5078 my ($self) = @_;
122             ($self->{_current_index} < $self->{_length})
123 37 100       80 || die "Out Of Bounds : no more elements";
124 36         50 $self->{_iterated} = 1;
125 36         67 return $self->_getItem($self->{_iteratee}, $self->{_current_index}++);
126             }
127              
128             sub get_next {
129 20     20 1 25 my ($self) = @_;
130 20         26 $self->{_iterated} = 1;
131 20 100       46 return undef unless ($self->{_current_index} < $self->{_length});
132 15         44 return $self->_getItem($self->{_iteratee}, $self->{_current_index}++);
133             }
134              
135 46     46 0 7258 sub getNext { my $self = shift; $self->get_next(@_) }
  46         92  
136              
137             sub peek {
138 31     31 1 602 my ($self, $n) = @_;
139              
140 31 100       65 if(not defined $n) { $n = 1 }
  22 100       27  
    100          
141 1         6 elsif(not $n) { die "peek(0) doesn't make sense, did you mean get_next()?" }
142 1         7 elsif($n < 0) { die "peek() with negative argument doesn't make sense, perhaps you should use a BiDirectional iterator" }
143              
144 29         43 my $idx = $self->{_current_index} + ($n - 1);
145              
146 29 100       64 return undef unless ($idx < $self->{_length});
147 24         38 return $self->_getItem($self->{_iteratee}, $idx);
148             }
149              
150             sub current {
151 19     19 1 43 my ($self) = @_;
152 19         43 return $self->_getItem($self->{_iteratee}, $self->currentIndex());
153             }
154              
155             sub current_index {
156 57     57 1 72 my ($self) = @_;
157 57 100       174 return ($self->{_current_index} != 0) ? $self->{_current_index} - 1 : 0;
158             }
159              
160 57     57 0 4355 sub currentIndex { my $self = shift; $self->current_index(@_) }
  57         95  
161              
162             sub get_length {
163 32     32 1 41 my ($self) = @_;
164 32         69 return $self->{_length};
165             }
166              
167 32     32 0 39 sub getLength { my $self = shift; $self->get_length(@_) }
  32         51  
168              
169             1;
170             # ABSTRACT: A simple class for iterating over Perl arrays
171              
172             __END__