File Coverage

blib/lib/List/AutoNumbered.pm
Criterion Covered Total %
statement 59 59 100.0
branch 18 18 100.0
condition 3 3 100.0
subroutine 21 21 100.0
pod 8 8 100.0
total 109 109 100.0


line stmt bran cond sub pod time code
1             package List::AutoNumbered;
2              
3 6     6   347154 use 5.006;
  6         70  
4 6     6   33 use strict;
  6         11  
  6         122  
5 6     6   27 use warnings;
  6         11  
  6         254  
6              
7             our $VERSION = '0.000008'; # TRIAL
8              
9             # Exports
10 6     6   2771 use parent 'Exporter';
  6         1904  
  6         41  
11             our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS);
12             BEGIN {
13 6     6   664 @EXPORT = qw(LSKIP);
14 6         12 @EXPORT_OK = qw(*TRACE); # can be localized
15 6         122 %EXPORT_TAGS = (
16             default => [@EXPORT],
17             all => [@EXPORT, @EXPORT_OK]
18             );
19             }
20              
21             # Imports
22 6     6   2819 use Getargs::Mixed;
  6         6311  
  6         4321  
23              
24             # Documentation ======================================================== {{{1
25              
26             =head1 NAME
27              
28             List::AutoNumbered - Add sequential numbers to lists while creating them
29              
30             =head1 SYNOPSIS
31              
32             This module adds sequential numbers to lists of lists so you don't have to
33             type all the numbers. Its original use case was for adding line numbers
34             to lists of testcases. For example:
35              
36             use List::AutoNumbered; # line 1
37             my $list = List::AutoNumbered->new(__LINE__); # line 2
38             $list->load("a")-> # line 3
39             ("b") # line 4
40             ("c") # line 5
41             ("d"); # line 6
42              
43             # Now $list->arr is [ [3,"a"], [4,"b"], [5,"c"], [6,"d"] ]
44              
45             In general, you can pass any number to the constructor. For example:
46              
47             use List::AutoNumbered;
48             use Test::More tests => 1;
49              
50             my $list = List::AutoNumbered->new; # First entry will be number 1
51             $list->load("a")-> # Yes, trailing arrow
52             ("b") # Magic! Don"t need any more arrows!
53             ("c")
54             ("d");
55              
56             is_deeply($list->arr, [
57             [1, "a"], [2, "b"], [3, "c"], [4, "d"]
58             ]); # Yes, it is!
59              
60             =cut
61              
62             our $TRACE = 0; # Documented below
63              
64             =head1 METHODS
65              
66             =cut
67              
68             # }}}1
69              
70             # Internal helpers {{{1
71              
72             # Defined-or
73 38 100   38   215 sub _dor { (defined $_[0]) ? $_[0] : $_[1] }
74              
75             # }}}1
76              
77             =head2 new
78              
79             Constructor. Basic usage options:
80              
81             $list = List::AutoNumbered->new(); # first list item is number 1
82             $list = List::AutoNumbered->new($num); # first list item is $num+1
83             $list = List::AutoNumbered->new(-at => $num); # ditto
84              
85             Each successive element
86             will have the next number, unless you say otherwise (e.g., using
87             L). Specifically, the first item in the list will be numbered
88             one higher than the number passed to the C constructor.
89              
90             Constructor parameters are processed using L, so positional
91             and named parameters are both OK.
92              
93             =head3 The C function
94              
95             You can give the constructor a "how" function that will make the list entry
96             for a single L or L call:
97              
98             $list = List::AutoNumbered->new(-how => sub { @_ });
99             # Jam everything together to make a flat array
100             $list = List::AutoNumbered->new(41, sub { @_ });
101             # Positional is OK, too.
102              
103             The C function is called as C. C<$num> is the
104             line number for L calls, or C for L calls.
105             C<@data> is whatever data you passed to C or C. For example,
106             the default C function is:
107              
108             sub how {
109             shift unless defined $_[0]; # add passes undef as the line number.
110             [@_] # Wrap everything in an arrayref.
111             }
112              
113             See C for examples of custom C functions.
114              
115             =cut
116              
117             sub new {
118 19     19 1 5070 my ($class, %args) = parameters('self',[qw(; at how)], @_);
119             my $self = bless {
120             num => _dor($args{at}, 0), # The last number used
121             arr => [], # The data
122             how => _dor($args{how}, # Routine to make a list item
123             sub {
124 28 100   28   63 shift unless defined $_[0]; # add passes undef
125             # as the line number.
126 28         64 [@_] # By default, just wrap it
127             }
128 19         1225 ),
129             }, $class;
130              
131             # Make a loader that adds an item and returns itself --- not $self.
132             # Note that $self is captured --- the loader function does not take
133             # a $self argument.
134 19     34   109 $self->{loader} = sub { $self->_L(@_); return $self->{loader} };
  34         99  
  34         69  
135              
136 19 100       141 print "# Created - now $self->{num}\n" if $TRACE;
137 19         74 return $self;
138             } #new()
139              
140             # Accessors {{{1
141              
142             =head2 size
143              
144             Returns the size of the array. Like C.
145              
146             =cut
147              
148 19     19 1 443 sub size { scalar @{ shift->{arr} }; }
  19         134  
149              
150             =head2 last
151              
152             Returns the index of the last element in the array. Like C<$#array>.
153              
154             =cut
155              
156 8     8 1 24 sub last { shift->size-1; }
157              
158             =head2 arr
159              
160             Returns a reference to the array being built. Please do not modify this
161             array directly until you are done loading it. List::AutoNumbered may not
162             work if you do.
163              
164             =cut
165              
166 33     33 1 2148 sub arr { shift->{arr}; }
167              
168             =head2 last_number
169              
170             Returns the current number stored by the instance. This is the number
171             of the most recently preceding L or L call.
172             This is B the number that will be given to the next record, since that
173             depends on whether or not the next record has a skip (L).
174              
175             =cut
176              
177 7     7 1 38 sub last_number { shift->{num} }
178              
179             # }}}1
180             # Loading {{{1
181              
182             =head2 load
183              
184             Push a new record with the next number on the front. Usage:
185              
186             $instance->load(whatever args you want to push);
187              
188             Or, if the current record isn't associated with the number immediately after
189             the previous record,
190              
191             $instance->load(LSKIP $n, args);
192              
193             where C<$n> is the number of lines between this C call and the last one.
194              
195             Returns a coderef that you can call to chain loads. For example, this works:
196              
197             $instance->load(...)->(...)(...)(...) ... ;
198             # You need an arrow ^^ here, but don't need any after that.
199              
200             =cut
201              
202 24     24 1 2031 sub load { goto &{ shift->{loader} } } # kick off loading
  24         106  
203              
204             # _L: Implementation of load()
205             sub _L {
206 34     34   51 my $self = shift;
207              
208 34 100       69 shift if $self->_update_lnum(@_); # Check for skipped lines from LSKIP()
209              
210 34         51 push @{ $self->{arr} }, $self->{how}->($self->{num}, @_);
  34         97  
211 34         108 return $self;
212             } #_L()
213              
214             =head2 add
215              
216             Add to the array being built, B inserting the number on the front.
217             Does increment the number and respect skips, for consistency.
218              
219             Returns the instance.
220              
221             =cut
222              
223             sub add { # just add it
224 8     8 1 138 my $self = shift;
225 8 100       21 shift if $self->_update_lnum(@_); # Check for skipped lines from LSKIP()
226 8         18 push @{ $self->{arr} }, $self->{how}->(undef, @_);
  8         31  
227 8         38 return $self;
228             } #add
229              
230             # }}}1
231             # Skipping {{{1
232              
233             =head1 FUNCTIONS
234              
235             =head2 LSKIP
236              
237             A convenience function to create a skipper. Prototyped as C<($)> so you can
238             use it conveniently with L:
239              
240             $instance->load(LSKIP 1, whatever args...);
241              
242             If you are using line numbers, the parameter to C should be the number
243             of lines above the current line and below the last L or
244             L call. For example:
245              
246             my $instance = List::AutoNumbered->new(__LINE__);
247             # A line
248             # Another one
249             $instance->load(LSKIP 2, # two comment lines between new() and here
250             'some data');
251              
252             =cut
253              
254             sub LSKIP ($) {
255 19     19 1 1606 List::AutoNumbered::Skipper->new(@_);
256             } #LSKIP()
257              
258             # _update_lnum: Increment the line number, and run a skip if there is one.
259             # Call from a method as:
260             # my $self = shift;
261             # shift if $self->_update_lnum(@_);
262              
263             sub _update_lnum {
264 42     42   108 my $self = shift;
265              
266 42 100       120 if(ref $_[0] eq 'List::AutoNumbered::Skipper') { # implies scalar @_
267 12         40 $self->{num} += $_[0]->{how_many} + 1;
268 12 100       56 print "# Skipped $_[0]->{how_many} - now $self->{num}\n" if $TRACE;
269 12         36 return 1; # We do need to shift
270              
271             } else {
272 30         51 ++$self->{num};
273 30 100       105 print "# Incremented - now $self->{num}\n" if $TRACE;
274 30         82 return 0; # No skipper, so don't shift it off.
275             }
276             } #_update_lnum()
277              
278             =head1 INTERNAL PACKAGES
279              
280             =head2 List::AutoNumbered::Skipper
281              
282             This package represents a skip and is created by L.
283             No user-serviceable parts inside.
284              
285             =cut
286              
287             {
288             package List::AutoNumbered::Skipper;
289 6     6   57 use Scalar::Util qw(looks_like_number);
  6         13  
  6         827  
290              
291             =head3 new
292              
293             Creates a new skipper. Parameters are for internal use only and are not
294             part of the public API.
295              
296             =cut
297              
298             sub new {
299 24     24   2344 my $class = shift;
300 24 100 100     228 die "Need a single number" unless @_==1 and looks_like_number $_[0];
301 14         79 bless {how_many => $_[0]}, $class;
302             }
303             } #List::AutoNumbered::Skipper
304              
305             # }}}1
306              
307             1; # End of List::AutoNumbered
308              
309             # Rest of the docs {{{1
310             __END__