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