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   121 use Object::Simple -base;
  16         31  
  16         134  
3              
4 16     16   1957 use Carp 'croak';
  16         36  
  16         886  
5 16     16   7079 use DBIx::Custom::Util qw/_array_to_hash _subname _deprecate/;
  16         42  
  16         39684  
6              
7             has [qw/dbi sth/];
8             has stash => sub { {} };
9              
10             *all = \&fetch_hash_all;
11              
12             sub fetch {
13 170     170 1 1603 my $self = shift;
14            
15             # Fetch
16 170         1397 my @row = $self->{sth}->fetchrow_array;
17 170 100       545 return unless @row;
18              
19 133 100       369 if ($self->{_has_filter}) {
20             # Info
21 56 100       236 $self->_cache unless $self->{_cache};
22            
23             # Type rule
24 56 100 100     298 if ($self->{type_rule}->{from1} && !$self->{type_rule_off} && !$self->{type_rule1_off}) {
      100        
25 32         65 my $from = $self->{type_rule}->{from1};
26 32         97 for my $type (keys %$from) {
27 32         50 for my $column (@{$self->{_type_map}->{$type}}) {
  32         80  
28             $row[$_] = $from->{$type}->($row[$_])
29 35 50       127 for @{$self->{_pos}{$column} || []};
  35         181  
30             }
31             }
32             }
33 56 100 100     582 if ($self->{type_rule}->{from2} && !$self->{type_rule_off} && !$self->{type_rule2_off}) {
      100        
34 18         36 my $from = $self->{type_rule}->{from2};
35 18         45 for my $type (keys %$from) {
36 18         29 for my $column (@{$self->{_type_map}->{$type}}) {
  18         42  
37             $row[$_] = $from->{$type}->($row[$_])
38 18 50       31 for @{$self->{_pos}{$column} || []};
  18         96  
39             }
40             }
41             }
42            
43             # Filter
44 56 100       334 if ($self->{filter}) {
45 24         41 my @columns = keys %{$self->{filter}};
  24         79  
46            
47 24         58 for my $column (@columns) {
48 24         46 my $filter = $self->{filter}{$column};
49 24 50       50 next unless $filter;
50             $row[$_] = $filter->($row[$_])
51 24 50       40 for @{$self->{_pos}{$column} || []};
  24         109  
52             }
53             }
54             }
55            
56 133         605 \@row;
57             }
58              
59             sub fetch_hash {
60 1480     1480 1 3519 my $self = shift;
61            
62             # Fetch
63 1480 100       23606 return unless my $row = $self->{sth}->fetchrow_hashref;
64            
65 983 100       4003 if ($self->{_has_filter}) {
66            
67             # Info
68 65 100       402 $self->_cache unless $self->{_cache};
69            
70             # Type rule
71 65 100 100     327 if ($self->{type_rule}->{from1} &&
      66        
72             !$self->{type_rule_off} && !$self->{type_rule1_off})
73             {
74 29         55 my $from = $self->{type_rule}->{from1};
75 29         86 for my $type (keys %$from) {
76             $from->{$type} and $row->{$_} = $from->{$type}->($row->{$_})
77 29   66     47 for @{$self->{_type_map}->{$type}};
  29         153  
78             }
79             }
80 65 0 33     464 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       158 if ($self->{filter}) {
91 15         28 my @columns = keys %{$self->{filter}};
  15         54  
92            
93 15         38 for my $column (@columns) {
94 15 50       48 next unless exists $row->{$column};
95 15         32 my $filter = $self->{filter}->{$column};
96 15 50       66 $row->{$column} = $filter->($row->{$column}) if $filter;
97             }
98             }
99             }
100            
101 983         2524 $row;
102             }
103              
104             sub fetch_all {
105 15     15 1 76 my $self = shift;
106            
107             # Fetch all rows
108 15         32 my $rows = [];
109 15         47 while(my $row = $self->fetch) { push @$rows, $row}
  30         92  
110            
111 15         40 return $rows;
112             }
113              
114             sub fetch_hash_all {
115 473     473 1 2111 my $self = shift;
116            
117             # Fetch all rows as hash
118 473         965 my $rows = [];
119 473         1180 while(my $row = $self->fetch_hash) { push @$rows, $row }
  610         1680  
120            
121 473         1981 return $rows;
122             }
123              
124             sub fetch_hash_one {
125 314     314 1 17333 my $self = shift;
126            
127             # Fetch hash
128 314         806 my $row = $self->fetch_hash;
129 314 100       788 return unless $row;
130            
131             # Finish statement handle
132 312         6810 $self->sth->finish;
133            
134 312         3931 return $row;
135             }
136              
137             sub fetch_hash_multi {
138 19     19 1 9817 my ($self, $count) = @_;
139            
140             # Fetch multiple rows
141 19 100       66 croak 'Row count must be specified ' . _subname
142             unless $count;
143            
144 17 100       54 return if $self->{_finished};
145              
146 12         25 my $rows = [];
147 12         41 for (my $i = 0; $i < $count; $i++) {
148 24         53 my $row = $self->fetch_hash;
149 24 100       63 unless ($row) {
150 5         17 $self->{_finished} = 1;
151 5         14 last;
152             }
153 19         54 push @$rows, $row;
154             }
155            
156 12 50       34 return unless @$rows;
157 12         33 return $rows;
158             }
159              
160             sub fetch_multi {
161 19     19 1 9775 my ($self, $count) = @_;
162            
163             # Row count not specified
164 19 100       69 croak 'Row count must be specified ' . _subname
165             unless $count;
166            
167 17 100       55 return if $self->{_finished};
168            
169             # Fetch multi rows
170 12         27 my $rows = [];
171 12         86 for (my $i = 0; $i < $count; $i++) {
172 24         65 my $row = $self->fetch;
173 24 100       57 unless ($row) {
174 5         16 $self->{_finished} = 1;
175 5         14 last;
176             }
177 19         58 push @$rows, $row;
178             }
179            
180 12 50       33 return unless @$rows;
181 12         35 return $rows;
182             }
183              
184              
185             sub fetch_one {
186 57     57 1 199 my $self = shift;
187            
188             # Fetch
189 57         1170 my $row = $self->fetch;
190 57 100       177 return unless $row;
191            
192             # Finish statement handle
193 51         1138 $self->sth->finish;
194            
195 51         816 return $row;
196             }
197              
198             sub filter {
199 63     63 1 782 my $self = shift;
200            
201 63         121 $self->{_has_filter} = 1;
202            
203             # Set
204 63 100       172 if (@_) {
205            
206             # Convert filter name to subroutine
207 33 100       122 my $filter = @_ == 1 ? $_[0] : [@_];
208 33         120 $filter = _array_to_hash($filter);
209 33         130 for my $column (keys %$filter) {
210 33         62 my $fname = $filter->{$column};
211 33 100 33     320 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       351 unless exists $self->dbi->filters->{$fname};
217 15         592 $filter->{$column} = $self->dbi->filters->{$fname};
218             }
219             }
220            
221             # Merge
222 30         387 $self->{filter} = {%{$self->filter}, %$filter};
  30         90  
223            
224 30         107 return $self;
225             }
226            
227 30   50     189 return $self->{filter} ||= {};
228             }
229              
230             sub flat {
231 3     3 1 24 my $self = shift;
232            
233 3         8 my @flat;
234 3         16 while (my $row = $self->fetch) {
235 6         23 push @flat, @$row;
236             }
237 3         15 return @flat;
238             }
239              
240             sub kv {
241 6     6 1 54 my ($self, %opt) = @_;
242              
243 6         48 my $key_name = $self->{sth}{NAME}[0];
244 6         19 my $kv = {};
245 6         25 while (my $row = $self->fetch_hash) {
246 18         40 my $key_value = delete $row->{$key_name};
247 18 50       40 next unless defined $key_value;
248 18 100       44 if ($opt{multi}) {
249 12         44 _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         17 push @{$kv->{$key_value}}, $row;
  12         42  
253             }
254 6         21 else { $kv->{$key_value} = $row }
255             }
256            
257 6         23 return $kv;
258             }
259              
260             sub kvs {
261 3     3 1 27 my ($self, %opt) = @_;
262              
263 3         26 my $key_name = $self->{sth}{NAME}[0];
264 3         11 my $kv = {};
265 3         14 while (my $row = $self->fetch_hash) {
266 12         31 my $key_value = delete $row->{$key_name};
267 12 50       35 next unless defined $key_value;
268 12   100     46 $kv->{$key_value} ||= [];
269 12         18 push @{$kv->{$key_value}}, $row;
  12         36  
270             }
271            
272 3         12 return $kv;
273             }
274              
275 3     3 1 89 sub header { shift->sth->{NAME} }
276              
277             *one = \&fetch_hash_one;
278              
279             sub type_rule {
280 115     115 1 419 my $self = shift;
281            
282 115         207 $self->{_has_filter} = 1;
283            
284 115 50       242 if (@_) {
285 115 100       321 my $type_rule = ref $_[0] eq 'HASH' ? $_[0] : {@_};
286              
287             # From
288 115         272 for my $i (1 .. 2) {
289 230         908 $type_rule->{"from$i"} = _array_to_hash($type_rule->{"from$i"});
290 230 100       354 for my $data_type (keys %{$type_rule->{"from$i"} || {}}) {
  230         983  
291 139 50       365 croak qq{data type of from$i section must be lower case or number}
292             if $data_type =~ /[A-Z]/;
293 139         316 my $fname = $type_rule->{"from$i"}{$data_type};
294 139 100 100     665 if (defined $fname && ref $fname ne 'CODE') {
295             croak qq{Filter "$fname" is not registered" } . _subname
296 3 50       70 unless exists $self->dbi->filters->{$fname};
297            
298 3         131 $type_rule->{"from$i"}{$data_type} = $self->dbi->filters->{$fname};
299             }
300             }
301             }
302 115         235 $self->{type_rule} = $type_rule;
303            
304 115         240 return $self;
305             }
306            
307 0   0     0 return $self->{type_rule} || {};
308             }
309              
310             sub type_rule_off {
311 9     9 1 53 my $self = shift;
312 9         36 $self->{type_rule_off} = 1;
313 9         34 return $self;
314             }
315              
316             sub type_rule_on {
317 3     3 1 18 my $self = shift;
318 3         9 $self->{type_rule_off} = 0;
319 3         10 return $self;
320             }
321              
322             sub type_rule1_off {
323 3     3 1 21 my $self = shift;
324 3         10 $self->{type_rule1_off} = 1;
325 3         12 return $self;
326             }
327              
328             sub type_rule1_on {
329 3     3 1 20 my $self = shift;
330 3         9 $self->{type_rule1_off} = 0;
331 3         11 return $self;
332             }
333              
334             sub type_rule2_off {
335 3     3 1 19 my $self = shift;
336 3         9 $self->{type_rule2_off} = 1;
337 3         12 return $self;
338             }
339              
340             sub type_rule2_on {
341 3     3 1 19 my $self = shift;
342 3         9 $self->{type_rule2_off} = 0;
343 3         11 return $self;
344             }
345              
346             sub value {
347 6     6 1 14 my $self = shift;
348 6         21 my $row = $self->fetch_one;
349 6 100       21 my $value = $row ? $row->[0] : undef;
350 6         37 return $value;
351             }
352              
353             sub values {
354 3     3 1 9 my $self = shift;
355            
356 3         10 my $values = [];
357 3         12 my $rows = $self->fetch_all;
358 3         18 push @$values, $_->[0] for @$rows;
359 3         22 return $values;
360             }
361              
362             sub _cache {
363 112     112   194 my $self = shift;
364 112         255 $self->{_type_map} = {};
365 112         228 $self->{_pos} = {};
366 112         331 $self->{_columns} = {};
367 112 50       246 for (my $i = 0; $i < @{$self->{sth}->{NAME} || []}; $i++) {
  339         2485  
368 227         1186 my $type = lc $self->{sth}{TYPE}[$i];
369 227         1104 my $name = $self->{sth}{NAME}[$i];
370 227   100     1139 $self->{_type_map}{$type} ||= [];
371 227         358 push @{$self->{_type_map}{$type}}, $name;
  227         601  
372 227   100     897 $self->{_pos}{$name} ||= [];
373 227         308 push @{$self->{_pos}{$name}}, $i;
  227         510  
374 227         607 $self->{_columns}{$name} = 1;
375             }
376 112         388 $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