File Coverage

blib/lib/Data/Model.pm
Criterion Covered Total %
statement 305 318 95.9
branch 133 160 83.1
condition 36 51 70.5
subroutine 37 48 77.0
pod 7 36 19.4
total 518 613 84.5


line stmt bran cond sub pod time code
1             package Data::Model;
2              
3 74     74   2047737 use strict;
  74         161  
  74         2890  
4 74     74   387 use warnings;
  74         137  
  74         2913  
5             our $VERSION = '0.00008';
6              
7 74     74   413 use Carp ();
  74         135  
  74         2235  
8             $Carp::Internal{(__PACKAGE__)}++;
9              
10 74     74   39395 use Data::Model::Iterator;
  74         198  
  74         1931  
11 74     74   47117 use Data::Model::Transaction;
  74         204  
  74         5865  
12              
13             our $RUN_VALIDATION;
14             if (exists $ENV{DATA_MODE_RUN_VALIDATION}) {
15             $RUN_VALIDATION = $ENV{DATA_MODE_RUN_VALIDATION} ? 1 : 0;
16             } else {
17             $RUN_VALIDATION = 1; # default is any time validation
18             # $RUN_VALIDATION = $ENV{HARNESS_ACTIVE} ? 1 : 0;
19             }
20 74     74   70753 use Params::Validate ':all';
  74         1031426  
  74         303576  
21              
22              
23             ## for schema methods
24 0     0 0 0 sub driver {};
25 0     0 0 0 sub model {};
26 0     0 0 0 sub schema {};
27 0     0 0 0 sub column {};
28 0     0 0 0 sub columns {};
29 0     0 0 0 sub key {};
30 0     0 0 0 sub index {};
31 0     0 0 0 sub unique {};
32 0     0 0 0 sub schema_options {};
33 0     0   0 sub __properties { +{} }
34              
35             sub new {
36 67     67 1 9391 my $class = shift;
37 67         438 bless {
38             schema_class => $class,
39             }, $class;
40             }
41              
42             ## data model attributes
43              
44             sub get_schema_class {
45 0     0 0 0 my($self, $model) = @_;
46 0   0     0 (ref($self) || $self) . '::' . $model;
47             }
48              
49             sub get_schema {
50 1818     1818 0 33491 my($self, $model) = @_;
51 1818         8554 my $schema = $self->__properties->{schema}->{$model};
52 1818 50       6513 Carp::croak "not defined schema $model" unless $schema;
53 1818         4593 $schema;
54             }
55              
56             sub clear_all_drivers {
57 1     1 0 3 my $self = shift;
58 1         4 for my $model ($self->schema_names) {
59 3         15 $self->set_driver($model, undef);
60             }
61             }
62              
63             sub get_base_driver {
64 175     175 0 1661 shift->__properties->{base_driver};
65             }
66              
67             sub set_base_driver {
68 3     3 0 354 my($self, $driver) = @_;
69 3         14 $self->__properties->{base_driver} = $driver;
70 3         20 for my $model ($self->schema_names) {
71 7 100       21 $self->set_driver($model, $driver) unless $self->get_driver($model);
72             }
73             }
74              
75             sub get_driver {
76 20     20 0 3773 my($self, $model) = @_;
77 20         655 $self->get_schema($model)->{driver};
78             }
79              
80             sub set_driver {
81 9     9 0 39 my($self, $model, $driver) = @_;
82 9         21 my $schema = $self->get_schema($model);
83 9   66     79 my $old = (exists $schema->{driver} && $schema->{driver});
84 9 100       26 if ($old) {
85 5         41 $old->detach_model($model, $schema);
86             }
87 9         37 $schema->driver($driver);
88 9 100       27 if ($driver) {
89 6         36 $driver->attach_model($model, $schema);
90             }
91             }
92              
93              
94             sub schema_names {
95 38     38 0 83 my $self = shift;
96 38         75 keys %{ $self->__properties->{schema} };
  38         179  
97             }
98              
99             sub as_sqls {
100 33     33 0 1487 my $self = shift;
101 33         74 my $target = shift;
102 33         85 my @sql = ();
103 33         377 for my $model ($self->schema_names) {
104 83 50 66     385 next if $target && $model ne $target;
105 83         429 push @sql, $self->get_schema($model)->sql->as_sql;
106             }
107 33         286 @sql;
108             }
109              
110             ## get / set / delete
111              
112             sub _get_query_args {
113 668     668   1287 my $self = shift;
114 668         997 my $schema = shift;
115 668 100       2404 return [] unless exists $_[0];
116              
117             # get key array or query
118 640         967 my $key_array = undef;
119 640         933 my $query = undef;
120 640 100       3386 if (ref($_[0]) eq 'HASH') {
    100          
    50          
121             ## ->get( modelname => { search query } );
122 254         687 $query = shift;
123             } elsif (ref($_[0]) eq 'ARRAY') {
124             ## ->get( modelname => [ keys ]);
125 64         140 $key_array = shift;
126             } elsif (!ref($_[0])) {
127             ## ->get( modelname => 'key');
128 322         1070 $key_array = [ shift ];
129             } else {
130 0         0 return [];
131             }
132              
133             # get query
134 640 100       2446 if ($query) {
    100          
135             ## nop
136             } elsif (ref($_[0]) eq 'HASH') {
137             ## get query
138 20         48 $query = shift;
139             } else {
140 366         567 shift;
141             }
142              
143              
144             # for query param validation
145 640 100 100     4701 if ($RUN_VALIDATION && $query) {
146 272         492 my @p = %{ $query };
  272         1261  
147             validate(
148             @p, {
149             index => {
150             type => HASHREF | UNDEF,
151             optional => 1,
152             callbacks => {
153             has_index_name => sub {
154 114 50   114   1302 return 1 unless $_[0];
155 114 100       937 return 0 unless scalar(@{ [ %{ $_[0] } ] }) == 2;
  114         266  
  114         717  
156 112         264 my($name) = %{ $_[0] };
  112         354  
157 112         689 $schema->has_index($name);
158             },
159             },
160             },
161 272         16541 where => {
162             type => HASHREF | ARRAYREF | UNDEF,
163             optional => 1,
164             },
165             order => {
166             type => HASHREF | ARRAYREF | UNDEF,
167             optional => 1,
168             },
169             group => {
170             type => HASHREF | ARRAYREF | UNDEF,
171             optional => 1,
172             },
173             limit => {
174             type => SCALAR | UNDEF,
175             optional => 1,
176             },
177             offset => {
178             type => SCALAR | UNDEF,
179             optional => 1,
180             },
181             },
182             );
183             }
184              
185              
186             # if first key is undef then nothing keys
187 632 100 66     8376 $key_array = [] if $key_array && ref($key_array) && !defined $key_array->[0];
      100        
188              
189             # deflate search key
190 632 100       4337 if ($schema->has_deflate) {
191 132 100       425 if ($key_array) {
192 104         547 my $columns = $schema->get_columns_hash_by_key_array_and_hash(+{}, $key_array);
193 104         580 $schema->deflate($columns);
194 104         4182 $key_array = $schema->get_key_array_by_hash( $columns );
195             }
196              
197             # deflate search index
198 132 100 100     2736 if ($query && ref($query->{index}) eq 'HASH') {
199 20         120 my($name, $key_array) = ( %{ $query->{index} } );
  20         127  
200 20 100       94 $key_array = [ $key_array ] unless ref($key_array) eq 'ARRAY';
201 20         121 my $columns = $schema->get_columns_hash_by_key_array_and_hash(+{}, $key_array, $name);
202 20         114 $schema->deflate($columns);
203 20         938 $query->{index} = { $name => $schema->get_key_array_by_hash($columns, $name) };
204             }
205             }
206              
207 632 100 100     2524 return [] if ($key_array && !@{ $key_array });
  386         1685  
208 620 50 66     2785 return [] unless $key_array || $query;
209 620         2040 return [ $key_array, $query, @_ ];
210             }
211              
212             sub lookup {
213 184     184 1 26588 my($self, $model, $id) = @_;
214 184 100       1329 Carp::croak "The 'lookup' method can not be performed during a transaction." if $self->{active_transaction};
215 182         804 my $schema = $self->get_schema($model);
216 182 50       676 return unless $schema;
217              
218 182 100       801 $id = [ $id ] unless ref($id) eq 'ARRAY';
219              
220             # deflating
221 182         1086 my $id_hash = $schema->get_columns_hash_by_key_array_and_hash(+{}, $id);
222 182         1708 $schema->deflate($id_hash);
223 182         710 $id = $schema->get_key_array_by_hash( $id_hash );
224              
225 182         4519 Carp::confess 'The number of key is wrong'
226 182 100       410 unless scalar(@{ $id }) == scalar(@{ $schema->key });
  182         1204  
227              
228 178         1294 my $data = $schema->{driver}->lookup( $schema, $id );
229 178 100       645 return unless $data;
230              
231 162         249 my $obj = $data;
232 162 50       543 unless ($schema->{options}->{bare_row}) {
233 162         1121 $obj = $schema->new_obj($self, $data);
234 162         951 $schema->inflate($obj);
235 162         664 $schema->call_trigger('post_load', $obj);
236             }
237 162         9671 return $obj;
238             }
239              
240             sub lookup_multi {
241 62     62 1 44164 my($self, $model, $ids) = @_;
242 62 100       536 Carp::croak "The 'lookup_multi' method can not be performed during a transaction." if $self->{active_transaction};
243 60         269 my $schema = $self->get_schema($model);
244 60 50       1423 return unless $schema;
245              
246 60 100       221 $ids = [ $ids ] unless ref($ids) eq 'ARRAY';
247 60         88 my $id_size = scalar(@{ $schema->key });
  60         250  
248 60         110 my @id_list;
249 60         109 for my $id (@{ $ids }) {
  60         148  
250 140 100       499 $id = [ $id ] unless ref($id) eq 'ARRAY';
251              
252 140         1272 Carp::confess 'The number of key is wrong'
253 140 100       294 unless scalar(@{ $id }) == $id_size;
254              
255             # deflating
256 132         651 my $id_hash = $schema->get_columns_hash_by_key_array_and_hash(+{}, $id);
257 132         490 $schema->deflate($id_hash);
258 132         1140 $id = $schema->get_key_array_by_hash( $id_hash );
259              
260 132         451 push @id_list, $id;
261             }
262              
263 52         420 my $results = $schema->{driver}->lookup_multi( $schema, \@id_list );
264 52 100 66     374 return (undef) x scalar(@id_list) unless $results && ref($results) eq 'HASH';
265              
266 51         110 while (my($id, $data) = each %{ $results }) {
  163         573  
267 112         143 my $obj = $data;
268 112 100 66     824 unless ($schema->{options}->{bare_row} || !$obj) {
269 104         458 $obj = $schema->new_obj($self, $data);
270 104         419 $schema->inflate($obj);
271 104         1326 $schema->call_trigger('post_load', $obj);
272             }
273 112         3719 $results->{$id} = $obj;
274             }
275              
276 51         110 map { $results->{join("\0", @{ $_ })} } @id_list;
  130         163  
  130         812  
277             }
278              
279              
280             # $model->get( model_name => 'key' );
281             # $model->get( model_name => [qw/ key1 key2 /] );
282             # $model->get( model_name => 'key' => { query options ... });
283             # $model->set( model_name => { search query, ... } );
284             sub get {
285 583     583 1 194432 my $self = shift;
286 583 100       2605 Carp::croak "The 'get' method can not be performed during a transaction." if $self->{active_transaction};
287 581         2338 my $model = shift;
288 581         3364 my $schema = $self->get_schema($model);
289 581 50       1762 return unless $schema;
290              
291 581         3009 my $query = $self->_get_query_args($schema, @_);
292 573 100 100     2072 return if @_ && !@{ $query }; # undef key
  545         2237  
293 561         12339 local $schema->{schema_obj} = $self;
294 561         1426 my($iterator, $iterator_options) = $schema->{driver}->get( $schema, @{ $query } );
  561         4301  
295 561 100       1972 unless ($iterator) {
296 93 100       499 return if wantarray;
297 53         672 return Data::Model::Iterator::Empty->new;
298             }
299              
300 468 100       1813 if (wantarray) {
301 290         596 my @objs = ();
302 290         994 while (my $data = $iterator->()) {
303 326         837 my $obj = $data;
304 326 100       2457 unless ($schema->{options}->{bare_row}) {
305 324         1559 $obj = $schema->new_obj($self, $data);
306 324         1631 $schema->inflate($obj);
307 324         2511 $schema->call_trigger('post_load', $obj);
308             }
309 326         18605 push @objs, $obj;
310             }
311 290 100 66     2605 $iterator_options->{end}->() if exists $iterator_options->{end} && ref($iterator_options->{end}) eq 'CODE';
312 290         3595 return @objs;
313             }
314 178         2713 return Data::Model::Iterator->new(
315             $iterator,
316             %{ $iterator_options },
317             wrapper => sub {
318 446 50   446   1561 return shift if $schema->{options}->{bare_row};
319 446         2049 my $obj = $schema->new_obj($self, shift);
320 446         3426 $schema->inflate($obj);
321 446         2100 $schema->call_trigger('post_load', $obj);
322 446         24526 $obj;
323             },
324 178         346 );
325             }
326              
327             sub get_multi {
328 2     2 0 10418 my $self = shift;
329 2 50       286 Carp::croak "The 'get_multi' method can not be performed during a transaction." if $self->{active_transaction};
330             }
331              
332              
333             # $model->set( model_name => 'key' );
334             # $model->set( model_name => [qw/ key1 key2 /] );
335             # $model->set( model_name => 'key' => { column => 'value', ... });
336             # $model->set( model_name => [qw/ key1 key2 /] => { column => 'value', ... } );
337             # $model->set( model_name => { column => 'value', ... } );
338             sub set {
339 527 100   527 1 884361 Carp::croak "The 'set' method can not be performed during a transaction." if $_[0]->{active_transaction};
340 525         3101 shift->_insert_or_replace(0, @_);
341             }
342              
343             sub replace {
344 18 100   18 0 1453 Carp::croak "The 'replace' method can not be performed during a transaction." if $_[0]->{active_transaction};
345 16         225 shift->_insert_or_replace(1, @_);
346             }
347              
348             sub _insert_or_replace {
349 541     541   1136 my $self = shift;
350 541         1009 my $is_replace = shift;
351 541         1086 my $model = shift;
352 541 100 66     7348 return $self->update($model, @_) if ref($model) && $model->isa('Data::Model::Row');
353 529         3740 my $schema = $self->get_schema($model);
354 529 50       1909 return unless $schema;
355             # return unless exists $_[0];
356              
357             # get key array
358 529         849 my $key_array;
359             my $columns;
360 529 100       2619 if (ref($_[0]) eq 'HASH') {
    100          
    50          
361             ## ->set( modelname => { key => value, ... } );
362 325         584 $columns = shift;
363 325         1981 $key_array = $schema->get_key_array_by_hash($columns);
364             } elsif (ref($_[0]) eq 'ARRAY') {
365             ## ->set( modelname => [ keys ] => { key => value, ... } );
366 78         176 $key_array = shift;
367             } elsif (!ref($_[0])) {
368             ## ->set( modelname => 'key' => { key => value, ... } );
369 126         403 $key_array = [ shift ];
370             } else {
371             # return;
372             }
373              
374             # get columns
375 529 100       1954 if ($columns) {
    100          
376             ## nop
377             } elsif (ref($_[0]) eq 'HASH') {
378             ## get hash columns data
379 112         357 my $hash = shift;
380 112         629 $columns = $schema->get_columns_hash_by_key_array_and_hash($hash, $key_array);
381             } else {
382 92         667 $columns = $schema->get_columns_hash_by_key_array_and_hash(+{}, $key_array);
383             }
384              
385             # deflate
386 529         2767 $schema->deflate($columns);
387 529         7486 $key_array = $schema->get_key_array_by_hash( $columns );
388              
389             # triggers
390 529         2564 $schema->call_trigger('pre_save', $columns);
391 529         32604 $schema->set_default($columns); # set default
392 529         2094 $schema->call_trigger('pre_insert', $columns);
393              
394 529         28328 local $schema->{schema_obj} = $self;
395 529 100       1699 my $method = $is_replace ? 'replace' : 'set';
396 529         4367 my $result = $schema->{driver}->$method( $schema, $key_array => $columns, @_ );
397 521 50       2606 return unless $result;
398              
399 521 100       9124 unless ($schema->{options}->{bare_row}) {
400 520         3376 my $obj = $schema->new_obj($self, $result);
401 520         4177 $schema->inflate($obj);
402 520         8588 $schema->call_trigger('post_load', $obj);
403 520         44191 return $obj;
404             }
405 1         5 return $result;
406             }
407              
408             sub set_multi {
409 2     2 0 1246 my $self = shift;
410 2 50       246 Carp::croak "The 'set_multi' method can not be performed during a transaction." if $self->{active_transaction};
411             }
412              
413              
414             sub _get_schema_by_row {
415 194     194   554 my($self, $row) = @_;
416              
417 194         383 my $class = ref($row);
418 194 50       589 return unless $class;
419              
420 194         2640 my($klass, $model) = $class =~ /^(.+)::([^:]+)$/;
421 194 50 33     1491 return unless (ref($self) || $self) eq $klass;
422 194         804 return $self->get_schema($model);
423             }
424              
425             sub update {
426 77     77 1 5276 my $self = shift;
427 77 100       638 Carp::croak "The 'update' method can not be performed during a transaction." if $self->{active_transaction};
428 75         164 my $row = shift;
429 75 100 66     1885 return $self->update_direct($row, @_) unless ref($row) && $row->isa('Data::Model::Row');
430              
431 45         713 my $schema = $self->_get_schema_by_row($row);
432 45 50       178 return unless $schema;
433 45 100       97 return unless @{ $schema->{key} } > 0; # not has key
  45         410  
434              
435 41 50       285 return unless scalar(%{ $row->get_changed_columns });
  41         344  
436              
437 41         330 my $columns = $row->get_columns;
438 41         168 my $changed_columns = $row->get_changed_columns;
439 41         90 my $old_columns = +{ %{ $columns }, %{ $changed_columns } };
  41         156  
  41         282  
440              
441 41 100       275 if ($schema->has_deflate) {
442             # deflate
443 12         80 $schema->deflate($columns);
444 12         437 $schema->deflate($old_columns);
445             }
446              
447 41         532 $schema->call_trigger('pre_save', $columns);
448 41         1701 $schema->call_trigger('pre_update', $columns, $old_columns);
449              
450 41         3011 my $key_array = $schema->get_key_array_by_hash($columns);
451 41         269 my $old_key_array = $schema->get_key_array_by_hash($old_columns);
452              
453 41         416 my $result = $schema->{driver}->update(
454             $schema, $old_key_array, $key_array, $old_columns, $columns, $changed_columns, @_
455             );
456 41         285 $row->{changed_cols} = +{};
457 41 50       367 return unless $result;
458              
459 41         504 $row;
460             }
461              
462              
463             # $model->update_direct( model_name => 'key', +{ querys }, +{ update columns } );
464             # $model->update_direct( model_name => [qw/ key1 key2 /], +{ querys }, +{ update columns } );
465             # $model->update_direct( model_name => +{ querys }, +{ update columns } );
466             # direct_update get しないで直接 updateする where の組み立ては get/delete と同じ
467             sub update_direct {
468 44     44 0 7004 my $self = shift;
469 44 100       737 Carp::croak "The 'update_direct' method can not be performed during a transaction." if $self->{active_transaction};
470 42         95 my $model = shift;
471              
472 42         187 my $schema = $self->get_schema($model);
473 42 50       179 return unless $schema;
474              
475 42         248 my $query = $self->_get_query_args($schema, @_);
476 42 50       88 return unless @{ $query };
  42         345  
477              
478 42         351 $schema->deflate($query->[2]);
479 42         655 $schema->call_trigger('pre_save', $query->[2]);
480 42         1670 $schema->call_trigger('pre_update', $query->[2]);
481              
482 42         1918 local $schema->{schema_obj} = $self;
483 42         107 $schema->{driver}->update_direct( $schema, @{ $query } );
  42         370  
484             }
485              
486              
487             # $model->delete( model_name => 'key' );
488             # $model->delete( model_name => [qw/ key1 key2 /] );
489             sub delete {
490 99     99 1 22226 my $self = shift;
491 99 100       675 Carp::croak "The 'delete' method can not be performed during a transaction." if $self->{active_transaction};
492 97         197 my $row = shift;
493 97 100 66     1337 return $self->delete_direct($row, @_) unless ref($row) && $row->isa('Data::Model::Row');
494              
495 52         339 my $schema = $self->_get_schema_by_row($row);
496 52 50       200 return unless $schema;
497 52 100       106 return unless @{ $schema->{key} } > 0; # not has key
  52         375  
498              
499 48         496 my $columns = $row->get_columns;
500 48         429 my $key_array = $schema->get_key_array_by_hash($columns);
501              
502 48         180 local $schema->{schema_obj} = $self;
503 48         1427 $schema->{driver}->delete( $schema, $key_array, @_ );
504             }
505              
506             sub delete_direct {
507 47     47 0 1065 my $self = shift;
508 47 100       366 Carp::croak "The 'delete_direct' method can not be performed during a transaction." if $self->{active_transaction};
509 45         97 my $model = shift;
510 45         174 my $schema = $self->get_schema($model);
511 45 50       155 return unless $schema;
512              
513 45         208 my $query = $self->_get_query_args($schema, @_);
514 45 50       78 return unless @{ $query };
  45         173  
515              
516 45         225 local $schema->{schema_obj} = $self;
517 45         160 $schema->{driver}->delete( $schema, @{ $query } );
  45         327  
518             }
519              
520             sub delete_multi {
521 2     2 0 1103 my $self = shift;
522 2 50       215 Carp::croak "The 'delete_multi' method can not be performed during a transaction." if $self->{active_transaction};
523             }
524              
525              
526             # for transactions
527             sub txn_scope {
528 35 100   35 0 23919 Carp::croak "The 'txn_scope' method can not be performed during a transaction." if $_[0]->{active_transaction};
529 33         362 Data::Model::Transaction->new( @_ );
530             }
531              
532             sub txn_begin {
533 35     35 0 1045 my $self = shift;
534 35 100       341 Carp::croak "The 'txn_begin' method can not be performed during a transaction." if $self->{active_transaction};
535 33         205 my $driver = $self->get_base_driver;
536 33 50       141 Carp::croak 'You cannot use transaction, Because base_driver is not set by schema.' unless $driver;
537 33         81 $self->{active_transaction} = 1;
538              
539 33         242 $driver->txn_begin;
540             }
541              
542             sub txn_rollback {
543 22     22 0 47 my $self = shift;
544 22         186 my $driver = $self->get_base_driver;
545              
546 22         148 $driver->txn_rollback;
547 22         164 $self->txn_end;
548 22         60 1;
549             }
550              
551             sub txn_commit {
552 11     11 0 22 my $self = shift;
553 11         38 my $driver = $self->get_base_driver;
554              
555 11         91 $driver->txn_commit;
556 11         146 $self->txn_end;
557 11         48 1;
558             }
559              
560             sub txn_end {
561 33     33 0 87 my $self = shift;
562 33         116 my $driver = $self->get_base_driver;
563 33         100 $self->{active_transaction} = 0;
564 33         329 $driver->txn_end;
565             }
566              
567              
568             1;
569             __END__