File Coverage

blib/lib/ActiveRecord/Simple.pm
Criterion Covered Total %
statement 366 462 79.2
branch 129 218 59.1
condition 48 104 46.1
subroutine 52 70 74.2
pod 34 34 100.0
total 629 888 70.8


line stmt bran cond sub pod time code
1             package ActiveRecord::Simple;
2              
3 11     11   334588 use 5.010;
  11         35  
4 11     11   51 use strict;
  11         18  
  11         185  
5 11     11   44 use warnings;
  11         21  
  11         389  
6              
7             our $VERSION = '0.94';
8              
9 11     11   3571 use utf8;
  11         122  
  11         43  
10 11     11   3530 use Encode;
  11         76565  
  11         623  
11 11     11   67 use Carp;
  11         19  
  11         459  
12 11     11   3976 use Storable qw/freeze/;
  11         25039  
  11         590  
13 11     11   2959 use Module::Load;
  11         8745  
  11         55  
14 11     11   494 use vars qw/$AUTOLOAD/;
  11         19  
  11         409  
15 11     11   119 use Scalar::Util qw/blessed/;
  11         20  
  11         586  
16              
17 11     11   3326 use ActiveRecord::Simple::Find;
  11         27  
  11         348  
18 11     11   2945 use ActiveRecord::Simple::Utils;
  11         24  
  11         335  
19 11     11   2687 use ActiveRecord::Simple::Connect;
  11         29  
  11         1317  
20              
21             our $connector;
22              
23              
24             sub new {
25 141     141 1 245 my $class = shift;
26 141 50       306 my $param = (scalar @_ > 1) ? {@_} : $_[0];
27              
28 141 100       519 my $accessors_fields = $class->can('_get_columns') ? $class->_get_columns : [];
29              
30 141 100       401 if ($class->can('_get_mixins')) {
31 84         102 my @keys = keys %{ $class->_get_mixins };
  84         117  
32 84         154 $class->_mk_ro_accessors(\@keys);
33             }
34 141         359 $class->_mk_accessors($accessors_fields);
35              
36 141 100       360 if ($class->can('_get_relations')) {
  0         0  
37 126         200 my $relations = $class->_get_relations();
38              
39 11     11   73 no strict 'refs';
  11         26  
  11         7599  
40              
41             RELNAME:
42 126         149 for my $relname ( keys %{ $relations } ) {
  126         235  
43 140         228 my $pkg_method_name = $class . '::' . $relname;
44              
45 140 100       438 next RELNAME if $class->can($pkg_method_name);
46              
47 15         45 *{$pkg_method_name} = sub {
48 15     15   41 my ($self, @objects) = @_;
49              
50              
51 15         41 my $rel = $class->_get_relations->{$relname};
52 15   33     58 my $fkey = $rel->{foreign_key} || $rel->{key};
53 15         26 my $relation = $relations->{$relname};
54 15 100       40 if (@objects) {
55 5 100       16 if ($relation->{type} eq 'many') {
    50          
56 4 100 66     24 if ($objects[0] && blessed $objects[0]) {
57 1         2 for my $object (@objects) {
58 1         3 my $fk = $relation->{params}{fk};
59 1         2 my $pk = $self->_get_primary_key;
60 1         3 $object->$fk($self->$pk);
61              
62 1         5 $object->save;
63             }
64             }
65             else {
66 3         5 my $rel_class = (%{ $rel->{class} })[1];
  3         8  
67             return $rel_class->_find_many_to_many({
68             root_class => $class,
69 3         7 m_class => (%{ $rel->{class} })[0],
  3         17  
70             self => $self,
71             where_statement => \@objects,
72             });
73             }
74             }
75             elsif ($relation->{type} eq 'one') {
76             OBJECT:
77 1         2 for my $object (@objects) {
78 1 50 33     5 next OBJECT unless ref $object && grep { $relation->{type} eq $_ } qw/one many/;
  2         7  
79              
80 1         4 $self->{"relation_instance_$relname"} = $object;
81 1 50       3 my $pk = $relation->{params}{pk} or next OBJECT;
82 1 50       3 my $fk = $relation->{params}{fk} or next OBJECT;
83              
84 1         3 $self->$fk($object->$pk);
85             }
86             }
87              
88 2         9 return $self;
89             }
90             ### else
91 10 100       38 if (!$self->{"relation_instance_$relname"}) {
92 8         18 my $rel = $class->_get_relations->{$relname};
93 8   33     26 my $fkey = $rel->{foreign_key} || $rel->{key};
94              
95 8         19 my $type = $rel->{type} . '_to_';
96             my $rel_class = ( ref $rel->{class} eq 'HASH' ) ?
97 4         11 ( %{ $rel->{class} } )[1]
98 8 100       26 : $rel->{class};
99              
100             #load $rel_class;
101              
102             ### TODO: check for relation existing
103 8         13 while (my ($rel_key, $rel_opts) = each %{ $rel_class->_get_relations }) {
  20         41  
104             my $rel_opts_class = (ref $rel_opts->{class} eq 'HASH') ?
105 6         16 (%{ $rel_opts->{class} })[1]
106 12 100       32 : $rel_opts->{class};
107 12 100       38 $type .= $rel_opts->{type} if $rel_opts_class eq $class;
108             }
109              
110 8 100 66     66 if ($type eq 'one_to_many' or $type eq 'one_to_one' or $type eq 'one_to_only') {
    50 66        
    100          
    50          
    0          
111 2         4 my $fkey = $rel->{params}{fk};
112 2         5 my $pkey = $rel->{params}{pk};
113              
114 2   33     7 $self->{"relation_instance_$relname"} =
115             $rel_class->find("$pkey = ?", $self->$fkey)->fetch // $rel_class;
116             }
117             elsif ($type eq 'only_to_one') {
118 0         0 my $fkey = $rel->{params}{fk};
119 0         0 my $pkey = $rel->{params}{pk};
120              
121 0         0 $self->{"relation_instance_$relname"} =
122             $rel_class->find("$fkey = ?", $self->$pkey)->fetch;
123             }
124             elsif ($type eq 'many_to_one') {
125 2 50       12 return $rel_class->new() if not $self->can('_get_primary_key');
126 2         6 my $fkey = $rel->{params}{fk};
127 2         4 my $pkey = $rel->{params}{pk};
128              
129 2         8 $self->{"relation_instance_$relname"}
130             = $rel_class->find("$fkey = ?", $self->$pkey);
131             }
132             elsif ( $type eq 'many_to_many' ) {
133             $self->{"relation_instance_$relname"} =
134             $rel_class->_find_many_to_many({
135             root_class => $class,
136 4         9 m_class => (%{ $rel->{class} })[0],
  4         41  
137             self => $self,
138             });
139             }
140             elsif ($type eq 'generic_to_generic') {
141 0         0 my %find_attrs;
142 0         0 while (my ($k, $v) = each %{ $rel->{key} }) {
  0         0  
143 0         0 $find_attrs{$v} = $self->$k;
144             }
145 0         0 $self->{"relation_instance_$relname"} =
146             $rel_class->find(\%find_attrs);
147             }
148             }
149              
150 10         60 $self->{"relation_instance_$relname"};
151             }
152 15         110 }
153 11     11   79 use strict 'refs';
  11         25  
  11         3806  
154             }
155              
156 141         353 $class->auto_save(0);
157              
158 141   100     411 return bless $param || {}, $class;
159             }
160              
161              
162             sub auto_load {
163 8     8 1 295 my ($class) = @_;
164              
165 8         26 my @class_name_parts = split q/::/, $class;
166 8         15 my $class_name = $class_name_parts[-1];
167              
168             my $table_name = join '-', map {
169 8         14 join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/)
  8         37  
  10         40  
  20         34  
170             } $class_name;
171 8         18 $table_name .= 's';
172              
173             # 0. check the name
174 8         30 my $table_info_sth = $class->dbh->table_info('', '%', $table_name, 'TABLE');
175 8 50       3273 $table_info_sth->fetchrow_hashref or croak "Can't find table '$table_name' in the database";
176              
177             # 1. columns list
178 8         44 my $column_info_sth = $class->dbh->column_info(undef, undef, $table_name, undef);
179 8         8801 my $cols = $column_info_sth->fetchall_arrayref({});
180 8         1015 my @columns = ();
181 8         35 push @columns, $_->{COLUMN_NAME} for @$cols;
182              
183             # 2. Primary key
184 8         23 my $primary_key_sth = $class->dbh->primary_key_info(undef, undef, $table_name);
185 8         6548 my $primary_key_data = $primary_key_sth->fetchrow_hashref;
186 8 100       132 my $primary_key = ($primary_key_data) ? $primary_key_data->{COLUMN_NAME} : undef;
187              
188             # 3. Foreign keys
189             # TODO
190              
191 8 50       58 $class->table_name($table_name) if $table_name;
192 8 100       40 $class->primary_key($primary_key) if $primary_key;
193 8 50       41 $class->columns(\@columns) if @columns;
194             }
195              
196             sub load_info {
197 0     0 1 0 carp '[DEPRECATED] This method is deprecated and will be remowed in the feature. Use method "auto_load" instead.';
198 0         0 $_[0]->auto_load;
199             }
200              
201             sub _mk_accessors {
202 141     141   220 my ($class, $fields) = @_;
203              
204 141         225 my $super = caller;
205 141 50       223 return unless $fields;
206              
207 11     11   69 no strict 'refs';
  11         19  
  11         1176  
208             FIELD:
209 141         225 for my $f (@$fields) {
210 608         934 my $pkg_accessor_name = $class . '::' . $f;
211 608 100       1611 next FIELD if $class->can($pkg_accessor_name);
212 50         143 *{$pkg_accessor_name} = sub {
213 94 100   94   240 if ( scalar @_ > 1 ) {
214 13         28 $_[0]->{$f} = $_[1];
215              
216 13         35 return $_[0];
217             }
218              
219 81         285 return $_[0]->{$f};
220             }
221 50         161 }
222 11     11   63 use strict 'refs';
  11         23  
  11         758  
223              
224 141         206 return 1;
225             }
226              
227             sub _mk_ro_accessors {
228 84     84   146 my ($class, $fields) = @_;
229              
230 84 50       124 return unless $fields;
231 84         134 my $super = caller;
232              
233 11     11   57 no strict 'refs';
  11         20  
  11         15550  
234             FIELD:
235 84         130 for my $f (@$fields) {
236 84         136 my $pkg_accessor_name = $class . '::' . $f;
237 84 100       326 next FIELD if $class->can($pkg_accessor_name);
238 1         5 *{$pkg_accessor_name} = sub {
239 2 50   2   5 croak "You can't change '$f': object is read-only"
240             if scalar @_ > 1;
241              
242 2         5 return $_[0]->{$f}
243 1         5 };
244             }
245             }
246              
247             sub connect {
248 4     4 1 27456 my ($class, $dsn, $username, $password, $options) = @_;
249              
250 4         8 eval { require DBIx::Connector };
  4         276  
251              
252             $options->{HandleError} = sub {
253 0     0   0 my ($error_message, $DBI_st) = @_;
254              
255 0 0       0 $error_message or return;
256 0         0 croak $error_message;
257              
258 4 50       36 } if ! exists $options->{HandleError};
259              
260 4 50       12 if ($@) {
261 4         27 $connector = ActiveRecord::Simple::Connect->new($dsn, $username, $password, $options);
262 4         15 $connector->db_connect;
263             }
264             else {
265 0         0 $connector = DBIx::Connector->new($dsn, $username, $password, $options);
266             }
267              
268 4         16 return 1;
269             }
270              
271             sub belongs_to {
272 9     9 1 64 my ($class, $rel_name, $rel_class, $params) = @_;
273              
274 9         27 my $new_relation = {
275             class => $rel_class,
276             type => 'one',
277             #params => $params
278             };
279              
280             my $primary_key = $params->{pk} ||
281             $params->{primary_key} ||
282 9   33     53 _guess(primary_key => $class);
283              
284             my $foreign_key = $params->{fk} ||
285             $params->{foreign_key} ||
286 9   33     36 _guess(foreign_key => $rel_class);
287              
288             $new_relation->{params} = {
289 9         29 pk => $primary_key,
290             fk => $foreign_key,
291             };
292              
293 9 50 33     48 if ($class->can('_get_table_schema') && $class->can('_get_primary_key')) {
294             ### load $rel_class;
295 0         0 $class->_get_table_schema->add_constraint(
296             type => 'foreign_key',
297             fields => $params, ### TODO: !!!this is wrong!!!
298             reference_fields => $class->_get_primary_key,
299             reference_table => $rel_class->_table_name,
300             on_delete => 'cascade'
301             );
302             }
303              
304 9         32 return $class->_append_relation($rel_name => $new_relation);
305             }
306              
307             sub has_many {
308 11     11 1 84 my ($class, $rel_name, $rel_class, $params) = @_;
309              
310 11         72 my $new_relation = {
311             class => $rel_class,
312             type => 'many',
313             };
314              
315 11   50     53 $params ||= {};
316             #my ($primary_key, $foreign_key);
317             my $primary_key = $params->{pk} ||
318             $params->{primary_key} ||
319 11   33     58 _guess(primary_key => $class);
320              
321             my $foreign_key = $params->{fk} ||
322             $params->{foreign_key} ||
323 11   33     43 _guess(foreign_key => $class);
324              
325             $new_relation->{params} = {
326 11         43 pk => $primary_key,
327             fk => $foreign_key,
328             };
329              
330 11         45 return $class->_append_relation($rel_name => $new_relation);
331             }
332              
333             sub _guess {
334 44     44   72 my ($what_key, $class) = @_;
335              
336 44 100       124 return 'id' if $what_key eq 'primary_key';
337              
338 22         28 eval { load $class };
  22         58  
339              
340 22         3360 my $table_name = $class->_table_name;
341 22 50       154 $table_name =~ s/s$// if $what_key eq 'foreign_key';
342              
343 22 50       106 return ($what_key eq 'foreign_key') ? "$table_name\_id" : undef;
344             }
345              
346             sub _delete_keys {
347 0     0   0 my ($self, $rx) = @_;
348              
349 0 0       0 map { delete $self->{$_} if $_ =~ $rx } keys %$self;
  0         0  
350             }
351              
352             sub has_one {
353 2     2 1 16 my ($class, $rel_name, $rel_class, $params) = @_;
354              
355 2         7 my $new_relation = {
356             class => $rel_class,
357             type => 'only',
358             };
359              
360 2   50     13 $params ||= {};
361             #my ($primary_key, $foreign_key);
362             my $primary_key = $params->{pk} ||
363             $params->{primary_key} ||
364 2   33     18 _guess(primary_key => $class);
365              
366             my $foreign_key = $params->{fk} ||
367             $params->{foreign_key} ||
368 2   33     12 _guess(foreign_key => $class);
369              
370             $new_relation->{params} = {
371 2         9 pk => $primary_key,
372             fk => $foreign_key,
373             };
374              
375             #$class->_mk_attribute_getter('_get_secondary_key', $key);
376             ### TODO: add schema constraints
377 2         12 $class->_append_relation($rel_name => $new_relation);
378             }
379              
380             sub as_sql {
381 0     0 1 0 my ($class, $producer_name, %args) = @_;
382              
383 0 0       0 eval { require SQL::Translator }
  0         0  
384             || croak('Please install SQL::Translator to use this feature.');
385              
386 0 0       0 $class->can('_get_table_schema')
387             or return;
388              
389 0         0 my $t = SQL::Translator->new;
390 0         0 my $schema = $t->schema;
391 0         0 $schema->add_table($class->_get_table_schema);
392              
393 0   0     0 $t->producer($producer_name || 'PostgreSQL', %args);
394              
395 0         0 return $t->translate;
396             }
397              
398             sub generic {
399 0     0 1 0 my ($class, $rel_name, $rel_class, $key) = @_;
400              
401 0         0 my $new_relation = {
402             class => $rel_class,
403             type => 'generic',
404             key => $key
405             };
406              
407 0         0 return $class->_append_relation($rel_name => $new_relation);
408             }
409              
410             sub _append_relation {
411 22     22   52 my ($class, $rel_name, $rel_hashref) = @_;
412              
413 22 100       117 if ($class->can('_get_relations')) {
414 7         19 my $relations = $class->_get_relations();
415 7         15 $relations->{$rel_name} = $rel_hashref;
416 7         17 $class->relations($relations);
417             }
418             else {
419 15         77 $class->relations({ $rel_name => $rel_hashref });
420             }
421              
422 22         74 return;
423             }
424              
425             sub columns {
426 18     18 1 95 my ($class, @args) = @_;
427              
428             #return if $class->can('_get_columns');
429              
430 18         36 my $columns = [];
431 18 100       53 if (scalar @args == 1) {
    50          
432 11         21 my $arg = shift @args;
433 11 50 33     56 if (ref $arg && ref $arg eq 'ARRAY') {
    0 0        
434 11         24 $columns = $arg;
435             }
436             elsif (ref $arg && ref $arg eq 'HASH') {
437 0         0 $columns = [keys %$arg];
438 0         0 $class->fields(%$arg);
439             }
440             else {
441             # just one column?
442 0         0 push @$columns, $arg;
443             }
444             }
445             elsif (scalar @args > 1) {
446 7         19 push @$columns, @args;
447             }
448              
449 18         44 $class->_mk_attribute_getter('_get_columns', $columns);
450             }
451              
452             sub mixins {
453 1     1 1 7 my ($class, %mixins) = @_;
454              
455 1         3 $class->_mk_attribute_getter('_get_mixins', \%mixins);
456             }
457              
458             sub fields {
459 0     0 1 0 my ($class, %fields) = @_;
460              
461 0 0       0 eval { require SQL::Translator }
  0         0  
462             || croak('Please install SQL::Translator to use this feature. ');
463              
464 0         0 my $sql_translator = SQL::Translator->new(no_comments => 1);
465 0         0 my $schema = $sql_translator->schema;
466 0         0 my $table = $schema->add_table(name => $class->_table_name);
467              
468             FIELD:
469 0         0 for my $field (keys %fields) {
470 0         0 $table->add_field(name => $field, %{ $fields{$field} });
  0         0  
471             }
472              
473 0         0 $class->_mk_attribute_getter('_get_table_schema', $table);
474 0         0 $class->columns([keys %fields]);
475             }
476              
477             sub index {
478 0     0 1 0 my ($class, $index_name, $fields) = @_;
479              
480 0 0       0 if ($class->can('_get_table_schema')) {
481 0         0 $class->_get_table_schema->add_index(
482             name => $index_name,
483             fields => $fields
484             );
485             }
486             }
487              
488             sub primary_key {
489 15     15 1 71 my ($class, $primary_key) = @_;
490              
491 15         38 $class->_mk_attribute_getter('_get_primary_key', $primary_key);
492 15 50       79 $class->_get_table_schema->primary_key($primary_key)
493             if $class->can('_get_table_schema')
494             }
495              
496             sub secondary_key {
497 0     0 1 0 my ($class, $key) = @_;
498              
499 0         0 $class->_mk_attribute_getter('_get_secondary_key', $key);
500             }
501              
502             sub table_name {
503 18     18 1 7067 my ($class, $table_name) = @_;
504              
505 18         98 $class->_mk_attribute_getter('_get_table_name', $table_name);
506             }
507              
508             sub _table_name {
509 37 100   37   102 my $class = ref $_[0] ? ref $_[0] : $_[0];
510              
511 37 50       92 croak 'Invalid data class' if $class =~ /^ActiveRecord::Simple/;
512              
513 37 100       196 my $table_name =
514             $class->can('_get_table_name') ?
515             $class->_get_table_name
516             : ActiveRecord::Simple::Utils::class_to_table_name($class);
517              
518 37         81 return $table_name;
519             }
520              
521             sub auto_save {
522 142     142 1 239 my ($class, $is_on) = @_;
523              
524 142 100       234 $is_on = 1 if not defined $is_on;
525              
526 142         262 $class->_mk_attribute_getter('_smart_saving_used', $is_on);
527             }
528              
529             sub use_smart_saving {
530 0     0 1 0 carp '[DEPRECATED] Method "use_smart_saving" is deprecated and will be removed in the future. Please, use "auto_save" method insted.';
531 0         0 $_[0]->auto_save;
532             }
533              
534             sub relations {
535 22     22 1 46 my ($class, $relations) = @_;
536              
537 22         49 $class->_mk_attribute_getter('_get_relations', $relations);
538             }
539              
540             sub _mk_attribute_getter {
541 216     216   359 my ($class, $method_name, $return) = @_;
542              
543 216         360 my $pkg_method_name = $class . '::' . $method_name;
544 216 100       841 if ( !$class->can($pkg_method_name) ) {
545 11     11   82 no strict 'refs';
  11         23  
  11         20374  
546 82     975   244 *{$pkg_method_name} = sub { $return };
  82         692  
  975         1925  
547             }
548             }
549              
550             sub dbh {
551 317     317 1 20185 my ($self, $dbh) = @_;
552              
553 317 100       563 if ($dbh) {
554 3 50       10 if ($connector) {
555 0         0 $connector->dbh($dbh);
556             }
557             else {
558 3         19 $connector = ActiveRecord::Simple::Connect->new();
559 3         11 $connector->dbh($dbh);
560             }
561             }
562              
563 317         675 return $connector->dbh;
564             }
565              
566             sub save {
567 11     11 1 24 my ($self) = @_;
568              
569             #return unless $self->dbh;
570 11 50       28 croak "Undefined database handler" unless $self->dbh;
571              
572             return 1 if $self->_smart_saving_used
573             and defined $self->{snapshoot}
574 11 0 33     40 and $self->{snapshoot} eq freeze $self->to_hash;
      33        
575              
576             croak 'Object is read-only'
577 11 50 33     33 if exists $self->{read_only} && $self->{read_only} == 1;
578              
579 11         21 my $save_param = {};
580 11         22 my $fields = $self->_get_columns;
581              
582 11 100       48 my $pkey = ($self->can('_get_primary_key')) ? $self->_get_primary_key : undef;
583              
584             FIELD:
585 11         25 for my $field (@$fields) {
586 41 100 100     156 next FIELD if defined $pkey && $field eq $pkey && !$self->{$pkey};
      100        
587 38 50 33     71 next FIELD if ref $field && ref $field eq 'HASH';
588 38         77 $save_param->{$field} = $self->{$field};
589             }
590              
591             ### Get additional fields from related objects:
592 11         38 for my $field (keys %$self) {
593 44 100       92 next unless ref $self->{$field};
594 3 50       9 next unless $self->can('_get_relations');
595 3 100       8 next unless grep { $_ eq $field } keys %{ $self->_get_relations };
  3         10  
  3         5  
596              
597 1 50       3 my $relation = $self->_get_relations->{$field} or next;
598 1 50 33     6 next unless $relation->{type} && $relation->{type} eq 'one';
599              
600 1         2 my $fk = $relation->{params}{fk};
601 1         2 my $pk = $relation->{params}{pk};
602              
603 1         3 $save_param->{$fk} = $self->{$field}->$pk;
604             }
605              
606 11         26 my $result;
607 11 100       24 if ($self->{isin_database}) {
608 4         23 $result = $self->_update($save_param);
609             }
610             else {
611 7         36 $result = $self->_insert($save_param);
612             }
613 11 50       434 $self->{need_to_save} = 0 if $result;
614 11 50       28 delete $self->{SQL} if $result;
615              
616 11 50       58 return (defined $result) ? $self : undef;
617             }
618              
619             sub update {
620 0     0 1 0 my ($self, $params) = @_;
621              
622 0         0 my $fields = $self->_get_columns();
623             FIELD:
624 0         0 for my $field (@$fields) {
625 0 0       0 next FIELD if ! exists $params->{$field};
626 0 0       0 next FIELD if ! $params->{$field};
627              
628 0         0 $self->$field($params->{$field});
629             }
630              
631 0         0 return $self;
632             }
633              
634             sub _insert {
635 7     7   17 my ($self, $param) = @_;
636              
637 7 50 33     16 return unless $self->dbh && $param;
638              
639 7         36 my $table_name = $self->_table_name;
640 7         35 my @field_names = grep { defined $param->{$_} } sort keys %$param;
  21         58  
641 7 50       36 my $primary_key = ($self->can('_get_primary_key')) ? $self->_get_primary_key :
    100          
642             ($self->can('_get_secondary_key')) ? $self->_get_secondary_key : undef;
643              
644 7         17 my $field_names_str = join q/, /, map { q/"/ . $_ . q/"/ } @field_names;
  20         64  
645              
646 7         18 my (@bind, @values_list);
647 7         16 for (@field_names) {
648 20 100       44 if (ref $param->{$_} eq 'SCALAR') {
649 1         2 push @values_list, ${ $param->{$_} };
  1         2  
650             }
651             else {
652 19         30 push @values_list, '?';
653 19         29 push @bind, $param->{$_};
654             }
655             }
656 7         19 my $values = join q/, /, @values_list;
657 7         10 my $pkey_val;
658 7         22 my $sql_stm = qq{
659             INSERT INTO "$table_name" ($field_names_str)
660             VALUES ($values)
661             };
662              
663 7 50       14 if ( $self->dbh->{Driver}{Name} eq 'Pg' ) {
664 0 0       0 if ($primary_key) {
665 0 0       0 $sql_stm .= ' RETURINIG ' . $primary_key if $primary_key;
666 0         0 $sql_stm = ActiveRecord::Simple::Utils::quote_sql_stmt($sql_stm, $self->dbh->{Driver}{Name});
667 0         0 $pkey_val = $self->dbh->selectrow_array($sql_stm, undef, @bind);
668             }
669             else {
670             my $sth = $self->dbh->prepare(
671             ActiveRecord::Simple::Utils::quote_sql_stmt($sql_stm, $self->dbh->{Driver}{Name})
672 0         0 );
673              
674 0         0 $sth->execute(@bind);
675             }
676             }
677             else {
678             my $sth = $self->dbh->prepare(
679             ActiveRecord::Simple::Utils::quote_sql_stmt($sql_stm, $self->dbh->{Driver}{Name})
680 7         23 );
681 7         506 $sth->execute(@bind);
682              
683 7 100 100     55 if ( $primary_key && defined $self->{$primary_key} ) {
684 3         26 $pkey_val = $self->{$primary_key};
685             }
686             else {
687 4         11 $pkey_val = $self->dbh->last_insert_id(undef, undef, $table_name, undef);
688             }
689             }
690              
691 7 50 66     63 if (defined $primary_key && $self->can($primary_key) && $pkey_val) {
      66        
692 6         21 $self->$primary_key($pkey_val);
693             }
694 7         15 $self->{isin_database} = 1;
695              
696 7         24 return $pkey_val;
697             }
698              
699             sub _update {
700 4     4   10 my ($self, $param) = @_;
701              
702 4 50 33     8 return unless $self->dbh && $param;
703              
704 4         17 my $table_name = $self->_table_name;
705 4         22 my @field_names = sort keys %$param;
706 4 0       19 my $primary_key = ($self->can('_get_primary_key')) ? $self->_get_primary_key :
    50          
707             ($self->can('_get_secondary_key')) ? $self->_get_secondary_key : undef;
708              
709 4         8 my (@set_list, @bind);
710 4         10 for (@field_names) {
711 17 50       29 if (ref $param->{$_} eq 'SCALAR') {
712 0         0 push @set_list, $_ . ' = ' . ${ $param->{$_} };
  0         0  
713             }
714             else {
715 17         33 push @set_list, "$_ = ?";
716 17         32 push @bind, $param->{$_};
717             }
718             }
719 4         12 my $setstring = join q/, /, @set_list;
720 4         10 push @bind, $self->{$primary_key};
721              
722             my $sql_stm = ActiveRecord::Simple::Utils::quote_sql_stmt(
723             qq{
724             UPDATE "$table_name" SET $setstring
725             WHERE
726             $primary_key = ?
727             },
728             $self->dbh->{Driver}{Name}
729 4         17 );
730              
731 4         14 return $self->dbh->do($sql_stm, undef, @bind);
732             }
733              
734             # param:
735             # cascade => 1
736             sub delete {
737 2     2 1 853 my ($self, $param) = @_;
738              
739 2 50       8 return unless $self->dbh;
740              
741 2         9 my $table_name = $self->_table_name;
742 2         5 my $pkey = $self->_get_primary_key;
743              
744 2 50       8 return unless $self->{$pkey};
745              
746 2         7 my $sql = qq{
747             DELETE FROM "$table_name" WHERE $pkey = ?
748             };
749 2 0 33     6 $sql .= ' CASCADE ' if $param && $param->{cascade};
750              
751 2         3 my $res = undef;
752 2         5 $sql = ActiveRecord::Simple::Utils::quote_sql_stmt($sql, $self->dbh->{Driver}{Name});
753              
754 2 50       6 if ( $self->dbh->do($sql, undef, $self->{$pkey}) ) {
755 2         126 $self->{isin_database} = undef;
756 2         5 delete $self->{$pkey};
757              
758 2         4 $res = 1;
759             }
760              
761 2         8 return $res;
762             }
763              
764             sub is_defined {
765 0     0 1 0 my ($self) = @_;
766              
767 0         0 return grep { defined $self->{$_} } @{ $self->_get_columns };
  0         0  
  0         0  
768             }
769              
770             # param:
771             # only_defined_fields => 1
772             ### TODO: refactor this
773             sub to_hash {
774 3     3 1 22 my ($self, $param) = @_;
775              
776 3         8 my $field_names = $self->_get_columns;
777 3 100       14 push @$field_names, keys %{ $self->_get_mixins } if $self->can('_get_mixins');
  2         3  
778 3         6 my $attrs = {};
779              
780 3         7 for my $field (@$field_names) {
781 15 50       28 next if ref $field;
782 15 100 66     34 if ( $param && $param->{only_defined_fields} ) {
783 7 100       13 $attrs->{$field} = $self->{$field} if defined $self->$field;
784             }
785             else {
786 8         15 $attrs->{$field} = $self->{$field};
787             }
788             }
789              
790 3         15 return $attrs;
791             }
792              
793             sub increment {
794 0     0 1 0 my ($self, @fields) = @_;
795              
796             FIELD:
797 0         0 for my $field (@fields) {
798 0 0       0 next FIELD if not exists $self->{$field};
799 0         0 $self->{$field} += 1;
800             }
801              
802 0         0 return $self;
803             }
804              
805             sub decrement {
806 0     0 1 0 my ($self, @fields) = @_;
807              
808             FIELD:
809 0         0 for my $field (@fields) {
810 0 0       0 next FIELD if not exists $self->{$field};
811 0         0 $self->{$field} -= 1;
812             }
813              
814 0         0 return $self;
815             }
816              
817             #### Find ####
818              
819 70     70 1 4904 sub find { ActiveRecord::Simple::Find->new(shift, @_) }
820 13     13 1 68 sub get { shift->find(@_)->fetch } ### TODO: move to Finder
821 0     0 1 0 sub count { ActiveRecord::Simple::Find->count(shift, @_) }
822              
823             sub exists {
824 0     0 1 0 my $first_arg = shift;
825              
826 0         0 my ($class, @search_criteria);
827 0 0       0 if (ref $first_arg) {
828             # FOXME: Ugly solution, need some beautifulness =)
829             # object method
830 0         0 $class = ref $first_arg;
831              
832 0 0       0 if ($class eq 'ActiveRecord::Simple::Find') {
833 0         0 return $first_arg->exists;
834             }
835             else {
836 0         0 return ActiveRecord::Simple::Find->new($class, $first_arg->to_hash({ only_defined_fields => 1 }))->exists;
837             }
838             }
839             else {
840 0         0 carp '[DEPRECATED] This way of using method "exists" is deprecated. Please, see documentation to know how does it work now.';
841 0         0 $class = $first_arg;
842 0         0 @search_criteria = @_;
843 0 0       0 return (defined $class->find(@search_criteria)->fetch) ? 1 : 0;
844             }
845              
846              
847             }
848              
849 0     0 1 0 sub first { croak '[DEPRECATED] Using method "first" as a class-method is deprecated. Sorry about that. Please, use "first" in this way: "Model->find->first".'; }
850 0     0 1 0 sub last { croak '[DEPRECATED] Using method "last" as a class-method is deprecated. Sorry about that. Please, use "last" in this way: "Model->find->last".'; }
851 6     6 1 22 sub select { ActiveRecord::Simple::Find->select(shift, @_) }
852              
853 7     7   28 sub _find_many_to_many { ActiveRecord::Simple::Find->_find_many_to_many(shift, @_) }
854              
855       0     sub DESTROY {}
856              
857             ### FIXME: this implementation is actually too slow, need much faster solution
858             sub AUTOLOAD {
859 2     2   7 my ($self, $param) = @_;
860              
861 2         3 my $sub = $AUTOLOAD; $sub =~ s/.*:://g;
  2         13  
862 2         4 my $error = "Unknown method: $sub";
863              
864 2 50       12 croak "Error while executing '$sub' method, '$self' is not a valid (blessed) object." unless blessed $self;
865 2 100       118 croak "Undefined object for method $sub: must be not undef" unless $param;
866              
867 1 50       10 croak $error unless $self->can('_get_relations');
868 1         2 my @many2manies;
869 1         3 my $relations = $self->_get_relations;
870              
871 1         1 my $subclass = undef;
872 1         2 my %class_options;
873 1         3 for my $relation (values %$relations) {
874 2 100 66     11 next unless $relation->{type} eq 'many' && ref $relation->{class} eq 'HASH';
875 1         2 ($subclass) = keys %{ $relation->{class} };
  1         3  
876 1 50       6 next if !$subclass->can('_get_relations');
877 1         3 my $relations2 = $subclass->_get_relations;
878              
879 1         3 for my $rel_name (keys %$relations2) {
880 2 50       4 next unless exists $relations2->{$rel_name};
881              
882 2         4 my $pk = $relations2->{$rel_name}{params}{pk};
883 2         3 my $fk = $relations2->{$rel_name}{params}{fk};
884              
885 2 50 33     8 next unless $pk && $fk;
886              
887 2 100       7 $class_options{$fk} = ($rel_name eq $sub) ? $param->$pk : $self->$pk;
888             }
889             }
890              
891 1         5 return $subclass->new(\%class_options);
892             }
893              
894             ### Private
895              
896             1;
897              
898             __END__;