File Coverage

blib/lib/DBIx/Custom/Mapper.pm
Criterion Covered Total %
statement 64 69 92.7
branch 39 48 81.2
condition 2 3 66.6
subroutine 9 9 100.0
pod 2 2 100.0
total 116 131 88.5


line stmt bran cond sub pod time code
1             package DBIx::Custom::Mapper;
2 16     16   111 use Object::Simple -base;
  16         34  
  16         93  
3              
4 16     16   8379 use DBIx::Custom::NotExists;
  16         45  
  16         430  
5              
6 16     16   100 use Carp 'confess';
  16         27  
  16         798  
7 16     16   106 use DBIx::Custom::Util qw/_subname _deprecate/;
  16         30  
  16         15457  
8              
9             # Carp trust relationship
10             push @DBIx::Custom::CARP_NOT, __PACKAGE__;
11              
12             has 'param';
13             has condition => sub {
14             sub { defined $_[0] && length $_[0] }
15             };
16             has pass => sub { [] };
17              
18             sub map {
19 81     81 1 276 my ($self, %rule) = @_;
20 81         1811 my $param = $self->param;
21 81         508 $rule{$_} = {key => $_} for @{$self->pass};
  81         1325  
22            
23             # Mapping
24 81         215 my $new_param = {};
25 81         220 for my $key (keys %rule) {
26            
27 156         414 my $mapping = $rule{$key};
28            
29             # Get mapping information
30 156         326 my $new_key;
31             my $value;
32 156         0 my $condition;
33            
34 156 100       408 if (ref $mapping eq 'ARRAY') {
    100          
    100          
    50          
35 57         102 $new_key = $mapping->[0];
36 57         73 $value = $mapping->[1];
37 57 50       126 $condition = ref $mapping->[2] eq 'HASH' ? $mapping->[2]->{condition} : $mapping->[2];
38             }
39             elsif (ref $mapping eq 'HASH') {
40 93         155 $new_key = $mapping->{key};
41 93         163 $value = $mapping->{value};
42 93         141 $condition = $mapping->{condition};
43             }
44             elsif (!ref $mapping) {
45 3         8 $new_key = $mapping;
46 3         24 _deprecate('0.24', qq/map method's string value "$mapping" is DEPRECATED. / .
47             qq/use {key => ...} syntax instead/);
48             }
49             elsif (ref $mapping eq 'CODE') {
50 3         8 $value = $mapping;
51 3         21 _deprecate('0.24', qq/map method's code reference value "$mapping" is DEPRECATED. / .
52             qq/use {value => ...} syntax instead/);
53             }
54            
55 156 100       312 $new_key = $key unless defined $new_key;
56 156   66     2084 $condition ||= $self->condition;
57 156         669 $condition = $self->_condition_to_sub($condition);
58              
59             # Map parameter
60 156 100       339 if (ref $condition eq 'CODE') {
    50          
61 129 100       246 if (ref $param->{$key} eq 'ARRAY') {
62 9         22 $new_param->{$new_key} = [];
63 9         21 for (my $i = 0; $i < @{$param->{$key}}; $i++) {
  27         81  
64             $new_param->{$new_key}->[$i]
65 18 100       42 = $condition->($param->{$key}->[$i]) ? $param->{$key}->[$i]
66             : DBIx::Custom::NotExists->singleton;
67             }
68             }
69             else {
70 120 100       263 if ($condition->($param->{$key})) {
71 93 100       270 if (defined $value) {
72 42 100       97 if (ref $value) {
73 33         84 $new_param->{$new_key} = $value->($param->{$key});
74             }
75             else {
76 9         38 $value =~ s//$param->{$key}/e;
  6         22  
77 9         43 $new_param->{$new_key} = $value;
78             }
79             }
80 51         165 else { $new_param->{$new_key} = $param->{$key} }
81             }
82             }
83             }
84             elsif ($condition eq 'exists') {
85 27 50       59 if (ref $param->{$key} eq 'ARRAY') {
86 0         0 $new_param->{$new_key} = [];
87 0         0 for (my $i = 0; $i < @{$param->{$key}}; $i++) {
  0         0  
88             $new_param->{$new_key}->[$i]
89 0 0       0 = exists $param->{$key}->[$i] ? $param->{$key}->[$i]
90             : DBIx::Custom::NotExists->singleton;
91             }
92             }
93             else {
94 27 100       66 if (exists $param->{$key}) {
95             $new_param->{$new_key} = defined $value
96 18 100       67 ? $value->($param->{$key}) : $param->{$key};
97             }
98             }
99             }
100 0         0 else { confess qq/Condition must be code reference or "exists" / . _subname }
101             }
102            
103 81         362 return $new_param;
104             }
105              
106             sub new {
107 81     81 1 254 my $self = shift->SUPER::new(@_);
108            
109             # Check attribute names
110 81         681 my @attrs = keys %$self;
111 81         167 for my $attr (@attrs) {
112 114 50       409 confess qq{"$attr" is invalid attribute name (} . _subname . ")"
113             unless $self->can($attr);
114             }
115            
116 81         845 return $self;
117             }
118              
119              
120             sub _condition_to_sub {
121 156     156   300 my ($self, $if) = @_;
122             $if = $if eq 'exists' ? $if
123 3     3   13 : $if eq 'defined' ? sub { defined $_[0] }
124 21 100   21   153 : $if eq 'length' ? sub { defined $_[0] && length $_[0] }
125 156 50       552 : ref $if eq 'CODE' ? $if
    100          
    100          
    100          
126             : undef;
127              
128 156 50       306 confess "You can must specify right value to C " . _subname
129             unless $if;
130            
131 156         301 return $if;
132             }
133              
134             1;
135              
136             =head1 NAME
137              
138             DBIx::Custom::Mapper - Mapper of parameter
139              
140             =head1 SYNOPSYS
141              
142             my $mapper = $dbi->mapper(param => $param);
143             my $new_param = $mapper->map(
144             title => 'book.title', # Key
145             author => sub { '%' . $_[0] . '%'} # Value
146             price => ['book.price' => sub { '%' . $_[0] . '%' }], # Key and value
147             );
148              
149             =head1 ATTRIBUTES
150              
151             =head2 param
152              
153             my $param = $mapper->param;
154             $mapper = $mapper->param({title => 'Perl', author => 'Ken'});
155              
156             Parameter.
157              
158             =head2 pass
159              
160             my $pass = $mapper->pass;
161             $mapper = $mapper->pass([qw/title author/]);
162              
163             the key and value is copied without change when C method is executed.
164              
165             =head2 condition
166              
167             my $condition = $mapper->condition;
168             $mapper = $mapper->condition('exists');
169              
170             Mapping condtion, default to C.
171              
172             You can set the following values to C.
173              
174             =over 4
175              
176             =item * exists
177            
178             condition => 'exists'
179              
180             If key exists, key and value is mapped.
181              
182             =item * defined
183              
184             condition => 'defined';
185              
186             If value is defined, key and value is mapped.
187              
188             =item * length
189              
190             condition => 'length';
191              
192             If value is defined and has length, key and value is mapped.
193              
194             =item * code reference
195              
196             condition => sub { defined $_[0] }
197              
198             You can set code reference to C.
199             The subroutine return true, key and value is mapped.
200              
201             =head1 METHODS
202              
203             L inherits all methods from L
204             and implements the following new ones.
205              
206             =head2 map
207              
208             my $new_param = $mapper->map(
209             price => {key => 'book.price'}
210             title => {value => '%%'}
211             author => ['book.author' => '%%']
212             );
213              
214             my $new_param = $mapper->map(
215             price => {key => 'book.price'}
216             title => {value => sub { '%' . shift . '%'}}
217             author => ['book.author' => sub { '%' . shift . '%'}]
218             );
219              
220             Map parameter in C attribute into new parameter.
221              
222             For example, if C is set to
223              
224             {
225             price => 1900,
226             title => 'Perl',
227             author => 'Ken',
228             issue_date => '2010-11-11'
229             }
230              
231             The following hash reference is returned.
232              
233             {
234             'book.price' => 1900,
235             title => '%Perl%',
236             'book.author' => '%Ken%',
237             }
238              
239             =over 2
240              
241             B
242              
243             =item * String => Hash reference
244              
245             # String => Hash reference
246             price => {key => 'book.price'}
247             title => {value => '%%'}
248             title => {value => sub { '%' . shift . '%'}}
249              
250             If C is used, only key name is mapped to new parameter
251              
252             # Rule
253             price => {key => 'book.price'}
254             # Parameter
255             price => 1900,
256             # New parameter
257             'book.price' => 1900,
258              
259             If C is used, only value is mapped to new parameter
260              
261             # Rule
262             title => {value => '%%'}
263             title => {value => sub { '%' . shift . '%'}}
264            
265             # Parameter
266             title => 'Perl',
267             # New parameter
268             title => '%Perl%',
269              
270             C>valueE> is replaced by original value.
271             You can use code reference to convert original value.
272              
273             =item * String => Array reference
274            
275             # String => Array reference
276             author => ['book.author' => '%%']
277              
278             Both key name and value is mapped to new parameter.
279             This is same as the following syntax.
280              
281             # Rule
282             {key => 'book.author', value => '%%'}
283              
284             =back
285              
286             By default, If the value has length, key and value is mapped.
287              
288             title => 'Perl' # Mapped
289             {title => '' } # Not mapped
290             {title => undef} # Not mapped
291             {} # Not mapped
292              
293             You can set change mapping condition by C attribute.
294              
295             $mapper->condition('defined');
296              
297             Or you can set C option for each key.
298              
299             my $new_param = $mapper->map(
300             price => {key => 'book.price', condition => 'defined'}]
301             title => {value => sub { '%' . $_[0] . '%'}, condition => 'defined'}
302             author => ['book.author', sub { '%' . $_[0] . '%'}, 'exists']
303             );
304              
305             If C attribute is set, the keys and value is copied without change.
306              
307             $mapper->pass([qw/title author/]);
308             my $new_param = $mapper->map(price => {key => 'book.price'});
309              
310             The following hash reference
311            
312             {title => 'Perl', author => 'Ken', price => 1900}
313              
314             is mapped to
315              
316             {title => 'Perl', author => 'Ken', 'book.price' => 1900}
317              
318             =cut