File Coverage

blib/lib/Spreadsheet/XlateExcel.pm
Criterion Covered Total %
statement 54 54 100.0
branch 20 20 100.0
condition 12 12 100.0
subroutine 8 8 100.0
pod 3 3 100.0
total 97 97 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   16673 use Carp::Assert::More;
  2         7242  
  2         239  
12 2     2   1681 use Spreadsheet::ParseExcel;
  2         111481  
  2         71  
13              
14             #
15             # Bitch.
16             #
17              
18 2     2   20 use warnings;
  2         7  
  2         45  
19 2     2   8 use strict;
  2         1  
  2         878  
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.03
32              
33             =cut
34              
35             our $VERSION = '0.03';
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' [, formatter => Spreadsheet::ParseExcel::Fmt->new })
77              
78             Ye constructor.
79              
80             Optional formatter attribute is a Spreadsheet::ParseExcel formatter instance.
81             Refer to L for more about such formatters.
82              
83             =cut
84              
85             sub new {
86 12     12 1 19846 my ( $class, $option ) = @_;
87              
88 12         37 assert_exists $option=>'file';
89 12         219 assert_nonblank $option->{file};
90 12         557 assert_defined -f $option->{file}, 'incoming file exists';
91              
92 12         91 bless { book_id => Spreadsheet::ParseExcel->new->parse ( $option->{file}, $option->{formatter} ) }, $class;
93             }
94              
95             =head2 xlate
96              
97             $self->xlate ({ for_each_row_do => sub { my ( $sheet_id, $row, $row_vs ) = @_ ; ... } })
98              
99             Applies C sub to each row of each sheet (unless filtered, see below) of the book.
100              
101             Options:
102              
103             =over
104              
105             =item *
106              
107             C: targets a given book sheet by name
108              
109             =item *
110              
111             C: targets a given book sheet by RE inclusion on name
112              
113             =item *
114              
115             C: targets a given book sheet by RE exclusion on name
116              
117             =item *
118              
119             C: targets columns via a listref of strings
120              
121             =item *
122              
123             C: targets columns via a listref of regular expressions
124              
125             =back
126              
127             Callback function gets called for each row, fed with L ID, row index and arrayref of row values parameters.
128              
129             Returns self.
130              
131             =cut
132              
133             sub xlate {
134 12     12 1 182459 my ( $self, $option ) = @_;
135              
136 12         60 assert_exists $option => 'for_each_row_do';
137              
138 12 100       304 assert_listref $option->{on_columns_heads_named} if exists $option->{on_columns_heads_named};
139 12 100       63 assert_listref $option->{on_columns_heads_like} if exists $option->{on_columns_heads_like};
140              
141 12 100   54   119 my $match = $option->{on_columns_heads_named} ? sub { $_[0] eq $_[1] } : sub { $_[0] =~ $_[1] };
  30         92  
  54         271  
142 12         21 my $targets;
143 12 100 100     76 if ( $option->{on_columns_heads_named} || $option->{on_columns_heads_like} ) {
144 5 100       14 $targets = [ $option->{on_columns_heads_named} ? @{$option->{on_columns_heads_named}} : @{$option->{on_columns_heads_like}} ];
  2         6  
  3         9  
145             }
146              
147 12         39 XLATE_LOOP : for my $sheet ( $self->book_id->worksheets ) {
148 36         316 my $sheet_name = $sheet->get_name;
149              
150 36 100 100     201 next if $option->{on_sheet_named} && $sheet_name ne $option->{on_sheet_named};
151 24 100 100     83 next if $option->{on_sheets_like} && $sheet_name !~ $option->{on_sheets_like};
152 22 100 100     85 next if $option->{on_sheets_unlike} && $sheet_name =~ $option->{on_sheets_unlike};
153              
154 21         50 my ( $row_min, $row_max ) = $sheet->row_range;
155 21         147 my ( $col_min, $col_max ) = $sheet->col_range;
156              
157 21         131 my @rows = $row_min .. $row_max;
158 21         42 my @cols = $col_min .. $col_max;
159              
160 21 100       39 if ( $targets ) {
161 7         6 my @matching_cols;
162              
163 7         12 for my $target ( @$targets ) {
164 21         28 push @matching_cols, map { $_->[0] } grep { $match->( $_->[1]->value, $target ) } grep { defined $_->[1] } map { [ $_, $sheet->get_cell ( $row_min, $_ ) ] } @cols;
  16         37  
  84         128  
  84         186  
  84         470  
165             }
166              
167 7         14 @cols = @matching_cols;
168             }
169              
170 21         35 for my $row ( @rows ) {
171 59 100       489 $option->{for_each_row_do}->( $sheet, $row, [ map { $_ ? $_->value : '' } map { $sheet->get_cell ( $row, $_ ) } @cols ] );
  185         847  
  185         885  
172             }
173             }
174              
175 12         60 $self;
176             }
177              
178             =head2 book_id
179              
180             my $book_id = $self->book_id ()
181              
182             Accessor to L instance ID.
183              
184             =cut
185              
186             sub book_id {
187 12     12 1 21 my ( $self ) = @_;
188              
189 12         62 $self->{book_id};
190             }
191              
192             #
193             # Documentation.
194             #
195              
196             =head1 AUTHOR
197              
198             Xavier Caron, C<< >>
199              
200             =head1 BUGS
201              
202             Please report any bugs or feature requests to C, or through
203             the web interface at L. I will be notified, and then you'll
204             automatically be notified of progress on your bug as I make changes.
205              
206             =head1 SUPPORT
207              
208             You can find documentation for this module with the perldoc command.
209              
210             perldoc Spreadsheet::XlateExcel
211              
212             You can also look for information at:
213              
214             =over 4
215              
216             =item * RT: CPAN's request tracker
217              
218             L
219              
220             =item * AnnoCPAN: Annotated CPAN documentation
221              
222             L
223              
224             =item * CPAN Ratings
225              
226             L
227              
228             =item * Search CPAN
229              
230             L
231              
232             =back
233              
234             Code is available through github (L).
235              
236             =head1 ACKNOWLEDGEMENTS
237              
238             To Kawai Takanori, Gabor Szabo and John McNamara, authors of cool L module.
239              
240             =head1 LICENSE AND COPYRIGHT
241              
242             Copyright 2010 Xavier Caron.
243              
244             This program is free software; you can redistribute it and/or modify it
245             under the terms of either: the GNU General Public License as published
246             by the Free Software Foundation; or the Artistic License.
247              
248             See http://dev.perl.org/licenses/ for more information.
249              
250             =cut
251              
252             #
253             # True.
254             #
255              
256             1;