File Coverage

blib/lib/DBIx/Custom/Result.pm
Criterion Covered Total %
statement 200 206 97.0
branch 70 86 81.4
condition 36 51 70.5
subroutine 26 26 100.0
pod 22 22 100.0
total 354 391 90.5


line stmt bran cond sub pod time code
1             package DBIx::Custom::Result;
2 16     16   100 use Object::Simple -base;
  16         27  
  16         98  
3              
4 16     16   1524 use Carp 'croak';
  16         27  
  16         668  
5 16     16   5508 use DBIx::Custom::Util qw/_array_to_hash _subname _deprecate/;
  16         32  
  16         31320  
6              
7             has [qw/dbi sth/];
8             has stash => sub { {} };
9              
10             *all = \&fetch_hash_all;
11              
12             sub fetch {
13 170     170 1 1369 my $self = shift;
14            
15             # Fetch
16 170         1075 my @row = $self->{sth}->fetchrow_array;
17 170 100       434 return unless @row;
18              
19 133 100       1083 if ($self->{_has_filter}) {
20             # Info
21 56 100       218 $self->_cache unless $self->{_cache};
22            
23             # Type rule
24 56 100 100     247 if ($self->{type_rule}->{from1} && !$self->{type_rule_off} && !$self->{type_rule1_off}) {
      100        
25 32         53 my $from = $self->{type_rule}->{from1};
26 32         69 for my $type (keys %$from) {
27 32         39 for my $column (@{$self->{_type_map}->{$type}}) {
  32         63  
28             $row[$_] = $from->{$type}->($row[$_])
29 35 50       76 for @{$self->{_pos}{$column} || []};
  35         131  
30             }
31             }
32             }
33 56 100 100     465 if ($self->{type_rule}->{from2} && !$self->{type_rule_off} && !$self->{type_rule2_off}) {
      100        
34 18         29 my $from = $self->{type_rule}->{from2};
35 18         36 for my $type (keys %$from) {
36 18         22 for my $column (@{$self->{_type_map}->{$type}}) {
  18         32  
37             $row[$_] = $from->{$type}->($row[$_])
38 18 50       23 for @{$self->{_pos}{$column} || []};
  18         73  
39             }
40             }
41             }
42            
43             # Filter
44 56 100       248 if ($self->{filter}) {
45 24         30 my @columns = keys %{$self->{filter}};
  24         68  
46            
47 24         42 for my $column (@columns) {
48 24         53 my $filter = $self->{filter}{$column};
49 24 50       41 next unless $filter;
50             $row[$_] = $filter->($row[$_])
51 24 50       32 for @{$self->{_pos}{$column} || []};
  24         86  
52             }
53             }
54             }
55            
56 133         431 \@row;
57             }
58              
59             sub fetch_hash {
60 1480     1480 1 2896 my $self = shift;
61            
62             # Fetch
63 1480 100       18139 return unless my $row = $self->{sth}->fetchrow_hashref;
64            
65 983 100       3180 if ($self->{_has_filter}) {
66            
67             # Info
68 65 100       232 $self->_cache unless $self->{_cache};
69            
70             # Type rule
71 65 100 100     243 if ($self->{type_rule}->{from1} &&
      66        
72             !$self->{type_rule_off} && !$self->{type_rule1_off})
73             {
74 29         49 my $from = $self->{type_rule}->{from1};
75 29         59 for my $type (keys %$from) {
76             $from->{$type} and $row->{$_} = $from->{$type}->($row->{$_})
77 29   66     38 for @{$self->{_type_map}->{$type}};
  29         126  
78             }
79             }
80 65 0 33     381 if ($self->{type_rule}->{from2} &&
      33        
81             !$self->{type_rule_off} && !$self->{type_rule2_off})
82             {
83 0         0 my $from = $self->{type_rule}->{from2};
84 0         0 for my $type (keys %{$self->{type_rule}->{from2}}) {
  0         0  
85             $from->{$type} and $row->{$_} = $from->{$type}->($row->{$_})
86 0   0     0 for @{$self->{_type_map}->{$type}};
  0         0  
87             }
88             }
89             # Filter
90 65 100       128 if ($self->{filter}) {
91 15         23 my @columns = keys %{$self->{filter}};
  15         49  
92            
93 15         31 for my $column (@columns) {
94 15 50       32 next unless exists $row->{$column};
95 15         26 my $filter = $self->{filter}->{$column};
96 15 50       50 $row->{$column} = $filter->($row->{$column}) if $filter;
97             }
98             }
99             }
100            
101 983         2023 $row;
102             }
103              
104             sub fetch_all {
105 15     15 1 60 my $self = shift;
106            
107             # Fetch all rows
108 15         24 my $rows = [];
109 15         34 while(my $row = $self->fetch) { push @$rows, $row}
  30         59  
110            
111 15         34 return $rows;
112             }
113              
114             sub fetch_hash_all {
115 473     473 1 1630 my $self = shift;
116            
117             # Fetch all rows as hash
118 473         714 my $rows = [];
119 473         897 while(my $row = $self->fetch_hash) { push @$rows, $row }
  610         1319  
120            
121 473         1466 return $rows;
122             }
123              
124             sub fetch_hash_one {
125 314     314 1 14163 my $self = shift;
126            
127             # Fetch hash
128 314         599 my $row = $self->fetch_hash;
129 314 100       612 return unless $row;
130            
131             # Finish statement handle
132 312         5856 $self->sth->finish;
133            
134 312         2998 return $row;
135             }
136              
137             sub fetch_hash_multi {
138 19     19 1 8201 my ($self, $count) = @_;
139            
140             # Fetch multiple rows
141 19 100       53 croak 'Row count must be specified ' . _subname
142             unless $count;
143            
144 17 100       40 return if $self->{_finished};
145              
146 12         21 my $rows = [];
147 12         33 for (my $i = 0; $i < $count; $i++) {
148 24         42 my $row = $self->fetch_hash;
149 24 100       47 unless ($row) {
150 5         12 $self->{_finished} = 1;
151 5         36 last;
152             }
153 19         45 push @$rows, $row;
154             }
155            
156 12 50       28 return unless @$rows;
157 12         28 return $rows;
158             }
159              
160             sub fetch_multi {
161 19     19 1 8411 my ($self, $count) = @_;
162            
163             # Row count not specified
164 19 100       51 croak 'Row count must be specified ' . _subname
165             unless $count;
166            
167 17 100       41 return if $self->{_finished};
168            
169             # Fetch multi rows
170 12         48 my $rows = [];
171 12         36 for (my $i = 0; $i < $count; $i++) {
172 24         44 my $row = $self->fetch;
173 24 100       50 unless ($row) {
174 5         13 $self->{_finished} = 1;
175 5         10 last;
176             }
177 19         42 push @$rows, $row;
178             }
179            
180 12 50       38 return unless @$rows;
181 12         28 return $rows;
182             }
183              
184              
185             sub fetch_one {
186 57     57 1 143 my $self = shift;
187            
188             # Fetch
189 57         120 my $row = $self->fetch;
190 57 100       133 return unless $row;
191            
192             # Finish statement handle
193 51         929 $self->sth->finish;
194            
195 51         628 return $row;
196             }
197              
198             sub filter {
199 63     63 1 640 my $self = shift;
200            
201 63         101 $self->{_has_filter} = 1;
202            
203             # Set
204 63 100       134 if (@_) {
205            
206             # Convert filter name to subroutine
207 33 100       96 my $filter = @_ == 1 ? $_[0] : [@_];
208 33         88 $filter = _array_to_hash($filter);
209 33         103 for my $column (keys %$filter) {
210 33         55 my $fname = $filter->{$column};
211 33 100 33     186 if (exists $filter->{$column}
      66        
212             && defined $fname
213             && ref $fname ne 'CODE')
214             {
215             croak qq{Filter "$fname" is not registered" } . _subname
216 18 100       291 unless exists $self->dbi->filters->{$fname};
217 15         481 $filter->{$column} = $self->dbi->filters->{$fname};
218             }
219             }
220            
221             # Merge
222 30         315 $self->{filter} = {%{$self->filter}, %$filter};
  30         153  
223            
224 30         90 return $self;
225             }
226            
227 30   50     166 return $self->{filter} ||= {};
228             }
229              
230             sub flat {
231 3     3 1 16 my $self = shift;
232            
233 3         7 my @flat;
234 3         9 while (my $row = $self->fetch) {
235 6         17 push @flat, @$row;
236             }
237 3         12 return @flat;
238             }
239              
240             sub kv {
241 6     6 1 49 my ($self, %opt) = @_;
242              
243 6         37 my $key_name = $self->{sth}{NAME}[0];
244 6         16 my $kv = {};
245 6         16 while (my $row = $self->fetch_hash) {
246 18         27 my $key_value = delete $row->{$key_name};
247 18 50       39 next unless defined $key_value;
248 18 100       28 if ($opt{multi}) {
249 12         40 _deprecate('0.28', "DBIx::Custom::Result::kv method's "
250             . 'multi option is DEPRECATED. use kvs method instead');
251 12   100     57 $kv->{$key_value} ||= [];
252 12         15 push @{$kv->{$key_value}}, $row;
  12         32  
253             }
254 6         17 else { $kv->{$key_value} = $row }
255             }
256            
257 6         18 return $kv;
258             }
259              
260             sub kvs {
261 3     3 1 20 my ($self, %opt) = @_;
262              
263 3         18 my $key_name = $self->{sth}{NAME}[0];
264 3         10 my $kv = {};
265 3         8 while (my $row = $self->fetch_hash) {
266 12         23 my $key_value = delete $row->{$key_name};
267 12 50       21 next unless defined $key_value;
268 12   100     38 $kv->{$key_value} ||= [];
269 12         16 push @{$kv->{$key_value}}, $row;
  12         27  
270             }
271            
272 3         9 return $kv;
273             }
274              
275 3     3 1 65 sub header { shift->sth->{NAME} }
276              
277             *one = \&fetch_hash_one;
278              
279             sub type_rule {
280 115     115 1 340 my $self = shift;
281            
282 115         163 $self->{_has_filter} = 1;
283            
284 115 50       200 if (@_) {
285 115 100       271 my $type_rule = ref $_[0] eq 'HASH' ? $_[0] : {@_};
286              
287             # From
288 115         210 for my $i (1 .. 2) {
289 230         652 $type_rule->{"from$i"} = _array_to_hash($type_rule->{"from$i"});
290 230 100       279 for my $data_type (keys %{$type_rule->{"from$i"} || {}}) {
  230         795  
291 139 50       289 croak qq{data type of from$i section must be lower case or number}
292             if $data_type =~ /[A-Z]/;
293 139         228 my $fname = $type_rule->{"from$i"}{$data_type};
294 139 100 100     498 if (defined $fname && ref $fname ne 'CODE') {
295             croak qq{Filter "$fname" is not registered" } . _subname
296 3 50       58 unless exists $self->dbi->filters->{$fname};
297            
298 3         108 $type_rule->{"from$i"}{$data_type} = $self->dbi->filters->{$fname};
299             }
300             }
301             }
302 115         190 $self->{type_rule} = $type_rule;
303            
304 115         197 return $self;
305             }
306            
307 0   0     0 return $self->{type_rule} || {};
308             }
309              
310             sub type_rule_off {
311 9     9 1 38 my $self = shift;
312 9         19 $self->{type_rule_off} = 1;
313 9         24 return $self;
314             }
315              
316             sub type_rule_on {
317 3     3 1 15 my $self = shift;
318 3         7 $self->{type_rule_off} = 0;
319 3         10 return $self;
320             }
321              
322             sub type_rule1_off {
323 3     3 1 15 my $self = shift;
324 3         10 $self->{type_rule1_off} = 1;
325 3         9 return $self;
326             }
327              
328             sub type_rule1_on {
329 3     3 1 16 my $self = shift;
330 3         7 $self->{type_rule1_off} = 0;
331 3         9 return $self;
332             }
333              
334             sub type_rule2_off {
335 3     3 1 16 my $self = shift;
336 3         6 $self->{type_rule2_off} = 1;
337 3         10 return $self;
338             }
339              
340             sub type_rule2_on {
341 3     3 1 14 my $self = shift;
342 3         6 $self->{type_rule2_off} = 0;
343 3         10 return $self;
344             }
345              
346             sub value {
347 6     6 1 13 my $self = shift;
348 6         19 my $row = $self->fetch_one;
349 6 100       16 my $value = $row ? $row->[0] : undef;
350 6         26 return $value;
351             }
352              
353             sub values {
354 3     3 1 7 my $self = shift;
355            
356 3         8 my $values = [];
357 3         8 my $rows = $self->fetch_all;
358 3         13 push @$values, $_->[0] for @$rows;
359 3         16 return $values;
360             }
361              
362             sub _cache {
363 112     112   170 my $self = shift;
364 112         190 $self->{_type_map} = {};
365 112         217 $self->{_pos} = {};
366 112         268 $self->{_columns} = {};
367 112 50       168 for (my $i = 0; $i < @{$self->{sth}->{NAME} || []}; $i++) {
  339         1745  
368 227         950 my $type = lc $self->{sth}{TYPE}[$i];
369 227         947 my $name = $self->{sth}{NAME}[$i];
370 227   100     979 $self->{_type_map}{$type} ||= [];
371 227         257 push @{$self->{_type_map}{$type}}, $name;
  227         455  
372 227   100     741 $self->{_pos}{$name} ||= [];
373 227         256 push @{$self->{_pos}{$name}}, $i;
  227         362  
374 227         496 $self->{_columns}{$name} = 1;
375             }
376 112         313 $self->{_cache} = 1;
377             }
378              
379             1;
380              
381             =head1 NAME
382              
383             DBIx::Custom::Result - Result of select statement
384              
385             =head1 SYNOPSIS
386              
387             # Result
388             my $result = $dbi->select(table => 'book');
389              
390             # Fetch a row and put it into array reference
391             while (my $row = $result->fetch) {
392             my $author = $row->[0];
393             my $title = $row->[1];
394             }
395            
396             # Fetch only a first row and put it into array reference
397             my $row = $result->fetch_one;
398            
399             # Fetch all rows and put them into array of array reference
400             my $rows = $result->fetch_all;
401              
402             # Fetch a row and put it into hash reference
403             while (my $row = $result->fetch_hash) {
404             my $title = $row->{title};
405             my $author = $row->{author};
406             }
407            
408             # Fetch only a first row and put it into hash reference
409             my $row = $result->fetch_hash_one;
410             my $row = $result->one; # Alias for "fetch_hash_one"
411            
412             # Fetch all rows and put them into array of hash reference
413             my $rows = $result->fetch_hash_all;
414             my $rows = $result->all; # Alias for "fetch_hash_all"
415              
416             =head1 ATTRIBUTES
417              
418             =head2 dbi
419              
420             my $dbi = $result->dbi;
421             $result = $result->dbi($dbi);
422              
423             L object.
424              
425             =head2 sth
426              
427             my $sth = $reuslt->sth
428             $result = $result->sth($sth);
429              
430             Statement handle of L.
431              
432             =head1 METHODS
433              
434             L inherits all methods from L
435             and implements the following new ones.
436              
437             =head2 all
438              
439             my $rows = $result->all;
440              
441             Same as fetch_hash_all.
442              
443             =head2 fetch
444              
445             my $row = $result->fetch;
446              
447             Fetch a row and put it into array reference.
448              
449             =head2 fetch_all
450              
451             my $rows = $result->fetch_all;
452              
453             Fetch all rows and put them into array of array reference.
454              
455             =head2 fetch_one
456              
457             my $row = $result->fetch_one;
458              
459             Fetch only a first row and put it into array reference,
460             and finish statement handle.
461              
462             =head2 fetch_hash
463              
464             my $row = $result->fetch_hash;
465              
466             Fetch a row and put it into hash reference.
467              
468             =head2 fetch_hash_all
469              
470             my $rows = $result->fetch_hash_all;
471              
472             Fetch all rows and put them into array of hash reference.
473              
474             =head2 fetch_hash_one
475            
476             my $row = $result->fetch_hash_one;
477              
478             Fetch only a first row and put it into hash reference,
479             and finish statement handle.
480              
481             =head2 fetch_hash_multi
482              
483             my $rows = $result->fetch_hash_multi(5);
484            
485             Fetch multiple rows and put them into array of hash reference.
486              
487             =head2 fetch_multi
488              
489             my $rows = $result->fetch_multi(5);
490            
491             Fetch multiple rows and put them into array of array reference.
492              
493             =head2 filter
494              
495             $result->filter(title => sub { uc $_[0] }, author => 'to_upper');
496             $result->filter([qw/title author/] => 'to_upper');
497              
498             Set filter for column.
499             You can use subroutine or filter name as filter.
500             This filter is executed after C filter.
501              
502             =head2 flat
503              
504             my @list = $result->flat;
505              
506             All values is added to flatten list.
507            
508             my @list = $dbi->select(['id', 'title'])->flat;
509              
510             C method return the following data.
511              
512             (1, 'Perl', 2, 'Ruby')
513              
514             You can create key-value pair easily.
515              
516             my %titles = $dbi->select(['id', 'title'])->flat;
517              
518             =head2 kv
519              
520             my $key_value = $result->kv;
521              
522             Get key-value pairs.
523              
524             my $books = $dbi->select(['id', 'title', 'author'])->kv;
525              
526             If C method return the following data:
527              
528             [
529             {id => 1, title => 'Perl', author => 'Ken'},
530             {id => 2, title => 'Ruby', author => 'Taro'}
531             ]
532              
533             C method return the following data.
534              
535             {
536             1 => {title => 'Perl', author => 'Ken'},
537             2 => {title => 'Ruby', author => 'Taro'}
538             }
539              
540             First column value become key.
541              
542             =head2 kvs
543              
544             my $key_values = $result->kvs;
545              
546             Get key-values pairs.
547              
548             my $books = $dbi->select(['author', 'title', 'price'])->kvs;
549              
550             If C method return the following data:
551              
552             [
553             {author => 'Ken', title => 'Perl', price => 1000},
554             {author => 'Ken', title => 'Good', price => 2000},
555             {author => 'Taro', title => 'Ruby', price => 3000}
556             {author => 'Taro', title => 'Sky', price => 4000}
557             ]
558              
559             C method return the following data.
560              
561             {
562             Ken => [
563             {title => 'Perl', price => 1000},
564             {title => 'Good', price => 2000}
565             ],
566             Taro => [
567             {title => 'Ruby', price => 3000},
568             {title => 'Sky', price => 4000}
569             ]
570             }
571              
572             =head2 header
573              
574             my $header = $result->header;
575              
576             Get header column names.
577              
578             =head2 one
579              
580             my $row = $result->one;
581              
582             Alias for C.
583              
584             =head2 stash
585              
586             my $stash = $result->stash;
587             my $foo = $result->stash->{foo};
588             $result->stash->{foo} = $foo;
589              
590             Stash is hash reference to save some data.
591              
592             =head2 type_rule
593            
594             # Merge type rule
595             $result->type_rule(
596             # DATE
597             9 => sub { ... },
598             # DATETIME or TIMESTAMP
599             11 => sub { ... }
600             );
601              
602             # Replace type rule(by reference)
603             $result->type_rule([
604             # DATE
605             9 => sub { ... },
606             # DATETIME or TIMESTAMP
607             11 => sub { ... }
608             ]);
609              
610             This is same as L's C's .
611              
612             =head2 type_rule_off
613              
614             $result = $result->type_rule_off;
615              
616             Turn C and C type rule off.
617             By default, type rule is on.
618              
619             =head2 type_rule_on
620              
621             $result = $result->type_rule_on;
622              
623             Turn C and C type rule on.
624             By default, type rule is on.
625              
626             =head2 type_rule1_off
627              
628             $result = $result->type_rule1_off;
629              
630             Turn C type rule off.
631             By default, type rule is on.
632              
633             =head2 type_rule1_on
634              
635             $result = $result->type_rule1_on;
636              
637             Turn C type rule on.
638             By default, type rule is on.
639              
640             =head2 type_rule2_off
641              
642             $result = $result->type_rule2_off;
643              
644             Turn C type rule off.
645             By default, type rule is on.
646              
647             =head2 type_rule2_on
648              
649             $result = $result->type_rule2_on;
650              
651             Turn C type rule on.
652             By default, type rule is on.
653              
654             =head2 value
655              
656             my $value = $result->value;
657              
658             Get first column's first value.
659              
660             my $count = $dbi->select('count(*)', table => 'book')->value;
661              
662             This is almost same as the following one.
663              
664             my $count = $dbi->select('count(*)')->fetch_one->[0];
665              
666             =head2 values
667              
668             my $values = $result->values;
669              
670             Get first column's values.
671              
672             my $tables = $dbi->select('show tables')->values;
673              
674             This is same as the following one.
675              
676             my $rows = $dbi->select('show tables')->fetch_all;
677             my $tables = [map { $_->[0] } @$rows];
678              
679             =cut