8 |
8 |
use Text::CSV;
|
9 |
9 |
use Params::Validate qw(:all);
|
10 |
10 |
use Rose::Object::MakeMethods::Generic scalar => [ qw(
|
11 |
|
file encoding sep_char quote_char escape_char header header_acc class
|
|
11 |
file encoding sep_char quote_char escape_char header dispatch class
|
12 |
12 |
numberformat dateformat _io _csv _objects _parsed _data _errors
|
13 |
13 |
) ];
|
14 |
14 |
|
... | ... | |
22 |
22 |
quote_char => { default => '"' },
|
23 |
23 |
escape_char => { default => '"' },
|
24 |
24 |
header => { type => ARRAYREF, optional => 1 },
|
25 |
|
header_acc => { type => HASHREF, optional => 1 },
|
|
25 |
dispatch => { type => HASHREF, optional => 1 },
|
26 |
26 |
file => 1,
|
27 |
27 |
encoding => 0,
|
28 |
28 |
class => 0,
|
... | ... | |
74 |
74 |
@{ $_[0]->_errors }
|
75 |
75 |
}
|
76 |
76 |
|
|
77 |
sub check_header {
|
|
78 |
$_[0]->_check_header;
|
|
79 |
}
|
|
80 |
|
77 |
81 |
# private stuff
|
78 |
82 |
|
79 |
83 |
sub _open_file {
|
... | ... | |
96 |
100 |
$self->header($header);
|
97 |
101 |
}
|
98 |
102 |
|
|
103 |
sub _check_header_for_class {
|
|
104 |
my ($self, %params) = @_;
|
|
105 |
my @errors;
|
|
106 |
|
|
107 |
return unless $self->class;
|
|
108 |
return $self->header;
|
|
109 |
|
|
110 |
for my $method (@{ $self->header }) {
|
|
111 |
next if $self->class->can($self->_real_method($method));
|
|
112 |
|
|
113 |
push @errors, [
|
|
114 |
$method,
|
|
115 |
undef,
|
|
116 |
"header field $method is not recognized",
|
|
117 |
undef,
|
|
118 |
0,
|
|
119 |
];
|
|
120 |
}
|
|
121 |
|
|
122 |
$self->_push_error(@errors);
|
|
123 |
|
|
124 |
return ! @errors;
|
|
125 |
}
|
|
126 |
|
99 |
127 |
sub _parse_data {
|
100 |
128 |
my ($self, %params) = @_;
|
101 |
129 |
my (@data, @errors);
|
... | ... | |
120 |
148 |
}
|
121 |
149 |
|
122 |
150 |
$self->_data(\@data);
|
123 |
|
$self->_errors(\@errors);
|
|
151 |
$self->_push_error(@errors);
|
124 |
152 |
|
125 |
|
return if @errors;
|
126 |
|
return \@data;
|
|
153 |
return ! @errors;
|
127 |
154 |
}
|
128 |
155 |
|
129 |
156 |
sub _encode_layer {
|
... | ... | |
141 |
168 |
for my $line (@{ $self->_data }) {
|
142 |
169 |
push @objs, $self->class->new(
|
143 |
170 |
map {
|
144 |
|
($self->header_acc && $self->header_acc->{$_}) || $_ => $line->{$_}
|
|
171 |
$self->_real_method($_) => $line->{$_}
|
145 |
172 |
} grep { $_ } keys %$line
|
146 |
173 |
);
|
147 |
174 |
}
|
... | ... | |
149 |
176 |
$self->_objects(\@objs);
|
150 |
177 |
}
|
151 |
178 |
|
|
179 |
sub _real_method {
|
|
180 |
my ($self, $arg) = @_;
|
|
181 |
($self->dispatch && $self->dispatch->{$arg}) || $arg;
|
|
182 |
}
|
|
183 |
|
152 |
184 |
sub _guess_encoding {
|
153 |
185 |
# won't fix
|
154 |
186 |
'utf-8';
|
155 |
187 |
}
|
156 |
188 |
|
|
189 |
sub _push_error {
|
|
190 |
my ($self, @errors) = @_;
|
|
191 |
my @new_errors = ($self->errors, @errors);
|
|
192 |
$self->_errors(\@new_errors);
|
|
193 |
}
|
|
194 |
|
157 |
195 |
|
158 |
196 |
1;
|
159 |
197 |
|
... | ... | |
175 |
213 |
sep_char => ',', # default ';'
|
176 |
214 |
quote_char => ''', # default '"'
|
177 |
215 |
header => [qw(id text sellprice word)] # see later
|
178 |
|
header_acc => { sellprice => 'sellprice_as_number' }
|
|
216 |
dispatch => { sellprice => 'sellprice_as_number' }
|
179 |
217 |
class => 'SL::DB::CsvLine', # if present, map lines to this
|
180 |
218 |
)
|
181 |
219 |
|
... | ... | |
191 |
229 |
most cases you will want those line to be parsed into hashes or even objects,
|
192 |
230 |
so this model just skips ahead and gives you objects.
|
193 |
231 |
|
194 |
|
Encoding autodetection is not easy, and should not be trusted. Try to avoid it if possible.
|
|
232 |
Encoding autodetection is not easy, and should not be trusted. Try to avoid it
|
|
233 |
if possible.
|
195 |
234 |
|
196 |
235 |
=head1 METHODS
|
197 |
236 |
|
... | ... | |
233 |
272 |
|
234 |
273 |
=item C<encoding>
|
235 |
274 |
|
236 |
|
Encoding of the CSV file. Note that this module does not do any encoding guessing.
|
237 |
|
Know what your data ist. Defaults to utf-8.
|
|
275 |
Encoding of the CSV file. Note that this module does not do any encoding
|
|
276 |
guessing. Know what your data ist. Defaults to utf-8.
|
238 |
277 |
|
239 |
278 |
=item C<sep_char>
|
240 |
279 |
|
... | ... | |
246 |
285 |
|
247 |
286 |
=item C<header> \@FIELDS
|
248 |
287 |
|
249 |
|
can be an array of columns, in this case the first line is not used as a
|
|
288 |
Can be an array of columns, in this case the first line is not used as a
|
250 |
289 |
header. Empty header fields will be ignored in objects.
|
251 |
290 |
|
252 |
|
=item C<header_acc> \%ACCESSORS
|
|
291 |
=item C<dispatch> \%ACCESSORS
|
253 |
292 |
|
254 |
293 |
May be used to map header fields to custom accessors. Example:
|
255 |
294 |
|
... | ... | |
273 |
312 |
|
274 |
313 |
[
|
275 |
314 |
offending raw input,
|
276 |
|
Text::CSV error code if present,
|
277 |
|
Text::CSV error diagnostics if present,
|
|
315 |
Text::CSV error code if T:C error, 0 else,
|
|
316 |
error diagnostics,
|
278 |
317 |
position in line,
|
279 |
318 |
estimated line in file,
|
280 |
319 |
]
|
... | ... | |
294 |
333 |
|
295 |
334 |
Encoding errors are not dealt with properly.
|
296 |
335 |
|
297 |
|
=item *
|
298 |
|
|
299 |
|
Errors are not gathered.
|
300 |
|
|
301 |
336 |
=back
|
302 |
337 |
|
|
338 |
=head1 TODO
|
|
339 |
|
|
340 |
Dispatch to child objects, like this:
|
|
341 |
|
|
342 |
$csv = SL::Helper::Csv->new(
|
|
343 |
file => ...
|
|
344 |
class => SL::DB::Part,
|
|
345 |
dispatch => [
|
|
346 |
makemodel => {
|
|
347 |
make_1 => make,
|
|
348 |
model_1 => model,
|
|
349 |
},
|
|
350 |
makemodel => {
|
|
351 |
make_2 => make,
|
|
352 |
model_2 => model,
|
|
353 |
},
|
|
354 |
]
|
|
355 |
);
|
|
356 |
|
303 |
357 |
=head1 AUTHOR
|
304 |
358 |
|
305 |
359 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
|
SL/Helper/Csv.pm: header_acc umbenannt in dispatch, Doku, check_header