File Coverage

blib/lib/Data/Validate/CSV/Table.pm
Criterion Covered Total %
statement 62 72 86.1
branch 8 14 57.1
condition 3 9 33.3
subroutine 16 17 94.1
pod 0 2 0.0
total 89 114 78.0


line stmt bran cond sub pod time code
1 2     2   27 use v5.12;
  2         8  
2 2     2   12 use strict;
  2         9  
  2         42  
3 2     2   10 use warnings;
  2         8  
  2         117  
4              
5             package Data::Validate::CSV::Table;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.003';
9              
10 2     2   15 use Moo;
  2         4  
  2         10  
11 2     2   632 use PerlX::Maybe;
  2         5  
  2         15  
12 2     2   91 use Data::Validate::CSV::Types -types;
  2         15  
  2         38  
13 2     2   11219 use Types::Common::Numeric qw(PositiveOrZeroInt);
  2         5  
  2         20  
14 2     2   854 use Types::Path::Tiny qw(Path);
  2         4  
  2         18  
15 2     2   677 use namespace::autoclean;
  2         4  
  2         14  
16              
17             has columns => (
18             is => 'rwp',
19             isa => ArrayRef[Column],
20             init_arg => undef,
21             );
22              
23             has has_header => (
24             is => 'lazy',
25             isa => Bool,
26             coerce => 1,
27 0     0   0 builder => sub { 0 },
28             );
29              
30             has skip_rows => (
31             is => 'ro',
32             isa => PositiveOrZeroInt,
33 1     1   13505 builder => sub { 0 },
34             );
35              
36             has skip_rows_after_header => (
37             is => 'ro',
38             isa => PositiveOrZeroInt,
39 1     1   56 builder => sub { 0 },
40             );
41              
42             has input => (
43             is => 'ro',
44             isa => FileHandle->plus_coercions(
45             Path, q{ $_->openr_utf8 },
46             ScalarRef, q{ do { require IO::String; IO::String->new($$_) } },
47             ),
48             coerce => 1,
49             required => 1,
50             );
51              
52             has schema => (
53             is => 'ro',
54             isa => Schema,
55             coerce => 1,
56             );
57              
58             has reader => (
59             is => 'lazy',
60             isa => CodeRef,
61             );
62              
63             my $implementation;
64             sub _build_reader {
65 1     1   14 my $self = shift;
66 1   33     7 $implementation ||= eval { require Text::CSV_XS; 'Text::CSV_XS' };
  1         939  
  1         18261  
67 1   33     5 $implementation ||= do { require Text::CSV; 'Text::CSV' };
  0         0  
  0         0  
68 1         6 my $csv = $implementation->new({
69             allow_whitespace => 1,
70             sep_char => q{,},
71             });
72 1     6   165 sub { $csv->getline($_[0]) };
  6         199  
73             }
74              
75             has _done_init => (
76             is => 'rw',
77             isa => Bool,
78             init_arg => undef,
79             default => !!0,
80             );
81              
82             has row_count => (
83             is => 'rwp',
84             isa => PositiveOrZeroInt,
85             default => 0,
86             );
87              
88             has column_class => (
89             is => 'ro',
90             isa => ClassName,
91             default => Column->class,
92             );
93              
94             has row_class => (
95             is => 'ro',
96             isa => ClassName,
97             default => Row->class,
98             );
99              
100             has _pkey_seen => (
101             is => 'ro',
102             isa => HashRef,
103             default => sub { +{} },
104             );
105              
106             sub get_row {
107 5     5 0 11 my $self = shift;
108 5 100       83 $self->_init unless $self->_done_init;
109            
110 5         138 my $raw = $self->reader->($self->input);
111 5 100       164 return unless $raw;
112            
113 4         13 my $n = $self->row_count;
114 4         68 $self->_set_row_count(++$n);
115            
116 4         282 my $row = $self->row_class->new(
117             columns => $self->columns,
118             column_class => $self->column_class,
119             raw_values => $raw,
120             row_number => $n,
121             maybe primary_key_columns => $self->schema->primary_key,
122             );
123            
124 4 50       9079 if ($row->primary_key_columns) {
125 0         0 my $str = $row->key_string;
126 0 0       0 if (my $seen = $self->_pkey_seen->{$str}) {
127 0         0 $row->report_error("Already seen primary key on row $seen");
128             }
129             else {
130 0         0 $self->_pkey_seen->{$str} = $n;
131             }
132             }
133            
134 4         16 return $row;
135             }
136              
137             sub _init {
138 1     1   14 my $self = shift;
139 1 50       16 return if $self->_done_init;
140 1         16 for (my $i = 0; $i < $self->skip_rows; ++$i) {
141 0         0 $self->reader->();
142             }
143 1 50       17 if ($self->has_header) {
144 1         16 my $columns = $self->schema->clone_columns;
145 1         155 my $header = $self->reader->($self->input);
146 1 50       39 if ($header) {
147 1         6 for my $i (0 .. $#$header) {
148 3   33     16 ($columns->[$i] ||= $self->column_class->new)->maybe_set_name($header->[$i]);
149             }
150 1         8 for (my $i = 0; $i < $self->skip_rows_after_header; ++$i) {
151 0         0 $self->reader->($self->input);
152             }
153             }
154 1         21 $self->_set_columns($columns);
155             }
156             else {
157 0         0 $self->_set_columns($self->schema->clone_columns);
158             }
159 1         75 $self->_done_init(1);
160             }
161              
162             sub all_rows {
163 1     1 0 48 my $self = shift;
164 1         3 my @rows;
165 1         5 while (my $row = $self->get_row) {
166 4         11 push @rows, $row;
167             }
168 1         6 return @rows;
169             }
170              
171             1;