File Coverage

blib/lib/Spreadsheet/XlateExcel.pm
Criterion Covered Total %
statement 53 53 100.0
branch 20 20 100.0
condition 12 12 100.0
subroutine 8 8 100.0
pod 3 3 100.0
total 96 96 100.0


line stmt bran cond sub pod time code
1             #
2             # Module.
3             #
4              
5             package Spreadsheet::XlateExcel;
6              
7             #
8             # Dependencies.
9             #
10              
11 2     2   33138 use Carp::Assert::More;
  2         14384  
  2         393  
12 2     2   3590 use Spreadsheet::ParseExcel;
  2         252680  
  2         76  
13              
14             #
15             # Bitch.
16             #
17              
18 2     2   32 use warnings;
  2         9  
  2         93  
19 2     2   11 use strict;
  2         5  
  2         1337  
20              
21             #
22             # Documentation.
23             #
24              
25             =head1 NAME
26              
27             Spreadsheet::XlateExcel - Trigger a callback subroutine on each row of an Excel spreadsheet
28              
29             =head1 VERSION
30              
31             Version 0.02
32              
33             =cut
34              
35             our $VERSION = '0.02';
36              
37             =head1 SYNOPSIS
38              
39             This modules triggers a callback subroutine on each row of an Excel spreadsheet.
40              
41             Wrote this simple module because I was fed up from writing the same boilerplate code ever when I had to mine spreadsheets for data.
42              
43             Operates on every sheet unless a given sheet is targeted by name, RE inclusion or RE exclusion.
44              
45             Operates on every column unless targeted by column head name or RE (inclusion).
46              
47             For example:
48              
49             use Spreadsheet::XlateExcel;
50              
51             my $id = Spreadsheet::XlateExcel->new ({ file => 'sheet.xls' });
52              
53             # rip odd rows of "Sheet2" sheet
54              
55             my $lol;
56              
57             $id->xlate ({
58             on_sheet_named => 'Sheet2',
59             for_each_row_do => sub {
60             my ( $sheet_id, $row, $row_vs ) = @_;
61              
62             push @$lol, $row_vs unless $row % 2;
63             },
64             });
65              
66             =head1 METHODS
67              
68             =cut
69              
70             #
71             # Methods.
72             #
73              
74             =head2 new
75              
76             my $id = Spreadsheet::XlateExcel->new ({ file => 'sheet.xls' })
77              
78             Ye constructor.
79              
80             =cut
81              
82             sub new {
83 12     12 1 23823 my ( $class, $option ) = @_;
84            
85 12         52 assert_exists $option=>'file';
86 12         268 assert_nonblank $option->{file};
87 12         446 assert_defined -f $option->{file}, 'incoming file exists';
88            
89 12         107 bless { book_id => Spreadsheet::ParseExcel->new->parse ( $option->{file} ) }, $class;
90             }
91              
92             =head2 xlate
93              
94             $self->xlate ({ for_each_row_do => sub { my ( $sheet_id, $row, $row_vs ) = @_ ; ... } })
95              
96             Applies C sub to each row of each sheet (unless filtered, see below) of the book.
97              
98             Options:
99              
100             =over
101              
102             =item *
103              
104             C: targets a given book sheet by name
105              
106             =item *
107              
108             C: targets a given book sheet by RE inclusion on name
109              
110             =item *
111              
112             C: targets a given book sheet by RE exclusion on name
113              
114             =item *
115              
116             C: targets columns via a listref of strings
117              
118             =item *
119              
120             C: targets columns via a listref of regular expressions
121              
122             =back
123              
124             Callback function gets called for each row, fed with L ID, row index and arrayref of row values parameters.
125              
126             Returns self.
127              
128             =cut
129              
130             sub xlate {
131 12     12 1 256697 my ( $self, $option ) = @_;
132            
133 12         63 assert_exists $option => 'for_each_row_do';
134            
135 12 100       371 assert_listref $option->{on_columns_heads_named} if exists $option->{on_columns_heads_named};
136 12 100       65 assert_listref $option->{on_columns_heads_like} if exists $option->{on_columns_heads_like};
137            
138 12 100   54   113 my $match = $option->{on_columns_heads_named} ? sub { $_[0] eq $_[1] } : sub { $_[0] =~ $_[1] };
  30         123  
  54         315  
139 12         21 my $targets;
140 12 100 100     77 if ( $option->{on_columns_heads_named} || $option->{on_columns_heads_like} ) {
141 5 100       16 $targets = [ $option->{on_columns_heads_named} ? @{$option->{on_columns_heads_named}} : @{$option->{on_columns_heads_like}} ];
  2         7  
  3         8  
142             }
143              
144 12         56 XLATE_LOOP : for my $sheet ( $self->book_id->worksheets ) {
145 36         411 my $sheet_name = $sheet->get_name;
146            
147 36 100 100     239 next if $option->{on_sheet_named} && $sheet_name ne $option->{on_sheet_named};
148 24 100 100     94 next if $option->{on_sheets_like} && $sheet_name !~ $option->{on_sheets_like};
149 22 100 100     68 next if $option->{on_sheets_unlike} && $sheet_name =~ $option->{on_sheets_unlike};
150            
151 21         69 my ( $row_min, $row_max ) = $sheet->row_range;
152 21         172 my ( $col_min, $col_max ) = $sheet->col_range;
153            
154 21         160 my @rows = $row_min .. $row_max;
155 21         42 my @cols = $col_min .. $col_max;
156            
157 21 100       43 if ( $targets ) {
158 7         12 my @matching_cols;
159            
160 7         10 for my $target ( @$targets ) {
161 21         64 push @matching_cols, map { $_->[0] } grep { $match->( $_->[1]->value, $target ) } map { [ $_, $sheet->get_cell ( $row_min, $_ ) ] } @cols;
  16         51  
  84         312  
  84         646  
162             }
163            
164 7         17 @cols = @matching_cols;
165             }
166            
167 21         42 for my $row ( @rows ) {
168 59 100       746 $option->{for_each_row_do}->( $sheet, $row, [ map { $_ ? $_->value : '' } map { $sheet->get_cell ( $row, $_ ) } @cols ] );
  185         1294  
  185         1336  
169             }
170             }
171            
172 12         60 $self;
173             }
174              
175             =head2 book_id
176              
177             my $book_id = $self->book_id ()
178              
179             Accessor to L instance ID.
180              
181             =cut
182              
183             sub book_id {
184 12     12 1 19 my ( $self ) = @_;
185            
186 12         85 $self->{book_id};
187             }
188              
189             #
190             # Documentation.
191             #
192              
193             =head1 AUTHOR
194              
195             Xavier Caron, C<< >>
196              
197             =head1 BUGS
198              
199             Please report any bugs or feature requests to C, or through
200             the web interface at L. I will be notified, and then you'll
201             automatically be notified of progress on your bug as I make changes.
202              
203             =head1 SUPPORT
204              
205             You can find documentation for this module with the perldoc command.
206              
207             perldoc Spreadsheet::XlateExcel
208              
209             You can also look for information at:
210              
211             =over 4
212              
213             =item * RT: CPAN's request tracker
214              
215             L
216              
217             =item * AnnoCPAN: Annotated CPAN documentation
218              
219             L
220              
221             =item * CPAN Ratings
222              
223             L
224              
225             =item * Search CPAN
226              
227             L
228              
229             =back
230              
231             Code is available through github (L).
232              
233             =head1 ACKNOWLEDGEMENTS
234              
235             To Kawai Takanori, Gabor Szabo and John McNamara, authors of cool L module.
236              
237             =head1 LICENSE AND COPYRIGHT
238              
239             Copyright 2010 Xavier Caron.
240              
241             This program is free software; you can redistribute it and/or modify it
242             under the terms of either: the GNU General Public License as published
243             by the Free Software Foundation; or the Artistic License.
244              
245             See http://dev.perl.org/licenses/ for more information.
246              
247             =cut
248              
249             #
250             # True.
251             #
252              
253             1;