| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package ActiveRecord::Simple; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 11 |  |  | 11 |  | 380706 | use 5.010; | 
|  | 11 |  |  |  |  | 42 |  | 
| 4 | 11 |  |  | 11 |  | 67 | use strict; | 
|  | 11 |  |  |  |  | 27 |  | 
|  | 11 |  |  |  |  | 244 |  | 
| 5 | 11 |  |  | 11 |  | 55 | use warnings; | 
|  | 11 |  |  |  |  | 30 |  | 
|  | 11 |  |  |  |  | 508 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.93'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 11 |  |  | 11 |  | 4207 | use utf8; | 
|  | 11 |  |  |  |  | 148 |  | 
|  | 11 |  |  |  |  | 52 |  | 
| 10 | 11 |  |  | 11 |  | 4023 | use Encode; | 
|  | 11 |  |  |  |  | 92376 |  | 
|  | 11 |  |  |  |  | 796 |  | 
| 11 | 11 |  |  | 11 |  | 84 | use Carp; | 
|  | 11 |  |  |  |  | 24 |  | 
|  | 11 |  |  |  |  | 602 |  | 
| 12 | 11 |  |  | 11 |  | 4513 | use Storable qw/freeze/; | 
|  | 11 |  |  |  |  | 30762 |  | 
|  | 11 |  |  |  |  | 663 |  | 
| 13 | 11 |  |  | 11 |  | 3598 | use Module::Load; | 
|  | 11 |  |  |  |  | 10480 |  | 
|  | 11 |  |  |  |  | 65 |  | 
| 14 | 11 |  |  | 11 |  | 554 | use vars qw/$AUTOLOAD/; | 
|  | 11 |  |  |  |  | 26 |  | 
|  | 11 |  |  |  |  | 468 |  | 
| 15 | 11 |  |  | 11 |  | 139 | use Scalar::Util qw/blessed/; | 
|  | 11 |  |  |  |  | 26 |  | 
|  | 11 |  |  |  |  | 658 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 11 |  |  | 11 |  | 4101 | use ActiveRecord::Simple::Find; | 
|  | 11 |  |  |  |  | 33 |  | 
|  | 11 |  |  |  |  | 371 |  | 
| 18 | 11 |  |  | 11 |  | 3373 | use ActiveRecord::Simple::Utils; | 
|  | 11 |  |  |  |  | 27 |  | 
|  | 11 |  |  |  |  | 322 |  | 
| 19 | 11 |  |  | 11 |  | 2723 | use ActiveRecord::Simple::Connect; | 
|  | 11 |  |  |  |  | 31 |  | 
|  | 11 |  |  |  |  | 1469 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | our $connector; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub new { | 
| 25 | 141 |  |  | 141 | 1 | 268 | my $class = shift; | 
| 26 | 141 | 50 |  |  |  | 313 | my $param = (scalar @_ > 1) ? {@_} : $_[0]; | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 141 | 100 |  |  |  | 571 | my $accessors_fields = $class->can('_get_columns') ? $class->_get_columns : []; | 
| 29 |  |  |  |  |  |  |  | 
| 30 | 141 | 100 |  |  |  | 443 | if ($class->can('_get_mixins')) { | 
| 31 | 84 |  |  |  |  | 105 | my @keys = keys %{ $class->_get_mixins }; | 
|  | 84 |  |  |  |  | 126 |  | 
| 32 | 84 |  |  |  |  | 157 | $class->_mk_ro_accessors(\@keys); | 
| 33 |  |  |  |  |  |  | } | 
| 34 | 141 |  |  |  |  | 409 | $class->_mk_accessors($accessors_fields); | 
| 35 |  |  |  |  |  |  |  | 
| 36 | 141 | 100 |  |  |  | 403 | if ($class->can('_get_relations')) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 37 | 126 |  |  |  |  | 224 | my $relations = $class->_get_relations(); | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 11 |  |  | 11 |  | 78 | no strict 'refs'; | 
|  | 11 |  |  |  |  | 21 |  | 
|  | 11 |  |  |  |  | 8409 |  | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | RELNAME: | 
| 42 | 126 |  |  |  |  | 171 | for my $relname ( keys %{ $relations } ) { | 
|  | 126 |  |  |  |  | 259 |  | 
| 43 | 140 |  |  |  |  | 242 | my $pkg_method_name = $class . '::' . $relname; | 
| 44 |  |  |  |  |  |  |  | 
| 45 | 140 | 100 |  |  |  | 463 | next RELNAME if $class->can($pkg_method_name); | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 15 |  |  |  |  | 49 | *{$pkg_method_name} = sub { | 
| 48 | 15 |  |  | 15 |  | 49 | my ($self, @objects) = @_; | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 15 |  |  |  |  | 42 | my $rel = $class->_get_relations->{$relname}; | 
| 52 | 15 |  | 33 |  |  | 68 | my $fkey = $rel->{foreign_key} || $rel->{key}; | 
| 53 | 15 |  |  |  |  | 30 | my $relation = $relations->{$relname}; | 
| 54 | 15 | 100 |  |  |  | 45 | if (@objects) { | 
| 55 | 5 | 100 |  |  |  | 17 | if ($relation->{type} eq 'many') { | 
|  |  | 50 |  |  |  |  |  | 
| 56 | 4 | 100 | 66 |  |  | 35 | if ($objects[0] && blessed $objects[0]) { | 
| 57 | 1 |  |  |  |  | 3 | for my $object (@objects) { | 
| 58 | 1 |  |  |  |  | 3 | my $fk = $relation->{params}{fk}; | 
| 59 | 1 |  |  |  |  | 3 | my $pk = $self->_get_primary_key; | 
| 60 | 1 |  |  |  |  | 4 | $object->$fk($self->$pk); | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 1 |  |  |  |  | 4 | $object->save; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  | else { | 
| 66 | 3 |  |  |  |  | 4 | my $rel_class = (%{ $rel->{class} })[1]; | 
|  | 3 |  |  |  |  | 10 |  | 
| 67 |  |  |  |  |  |  | return $rel_class->_find_many_to_many({ | 
| 68 |  |  |  |  |  |  | root_class      => $class, | 
| 69 | 3 |  |  |  |  | 7 | m_class         => (%{ $rel->{class} })[0], | 
|  | 3 |  |  |  |  | 22 |  | 
| 70 |  |  |  |  |  |  | self            => $self, | 
| 71 |  |  |  |  |  |  | where_statement => \@objects, | 
| 72 |  |  |  |  |  |  | }); | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  | elsif ($relation->{type} eq 'one') { | 
| 76 |  |  |  |  |  |  | OBJECT: | 
| 77 | 1 |  |  |  |  | 3 | for my $object (@objects) { | 
| 78 | 1 | 50 | 33 |  |  | 6 | next OBJECT unless ref $object && grep { $relation->{type} eq $_ } qw/one many/; | 
|  | 2 |  |  |  |  | 8 |  | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 1 |  |  |  |  | 4 | $self->{"relation_instance_$relname"} = $object; | 
| 81 | 1 | 50 |  |  |  | 4 | my $pk = $relation->{params}{pk} or next OBJECT; | 
| 82 | 1 | 50 |  |  |  | 4 | my $fk = $relation->{params}{fk} or next OBJECT; | 
| 83 |  |  |  |  |  |  |  | 
| 84 | 1 |  |  |  |  | 3 | $self->$fk($object->$pk); | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 2 |  |  |  |  | 10 | return $self; | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  | ### else | 
| 91 | 10 | 100 |  |  |  | 44 | if (!$self->{"relation_instance_$relname"}) { | 
| 92 | 8 |  |  |  |  | 17 | my $rel  = $class->_get_relations->{$relname}; | 
| 93 | 8 |  | 33 |  |  | 28 | my $fkey = $rel->{foreign_key} || $rel->{key}; | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 8 |  |  |  |  | 22 | my $type = $rel->{type} . '_to_'; | 
| 96 |  |  |  |  |  |  | my $rel_class = ( ref $rel->{class} eq 'HASH' ) ? | 
| 97 | 4 |  |  |  |  | 11 | ( %{ $rel->{class} } )[1] | 
| 98 | 8 | 100 |  |  |  | 32 | : $rel->{class}; | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | #load $rel_class; | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | ### TODO: check for relation existing | 
| 103 | 8 |  |  |  |  | 17 | while (my ($rel_key, $rel_opts) = each %{ $rel_class->_get_relations }) { | 
|  | 20 |  |  |  |  | 50 |  | 
| 104 |  |  |  |  |  |  | my $rel_opts_class = (ref $rel_opts->{class} eq 'HASH') ? | 
| 105 | 6 |  |  |  |  | 17 | (%{ $rel_opts->{class} })[1] | 
| 106 | 12 | 100 |  |  |  | 37 | : $rel_opts->{class}; | 
| 107 | 12 | 100 |  |  |  | 42 | $type .= $rel_opts->{type} if $rel_opts_class eq $class; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 8 | 100 | 66 |  |  | 76 | 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 |  |  |  |  | 5 | my $fkey = $rel->{params}{fk}; | 
| 112 | 2 |  |  |  |  | 5 | my $pkey = $rel->{params}{pk}; | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 2 |  | 33 |  |  | 10 | $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 |  |  |  |  | 8 | my $fkey = $rel->{params}{fk}; | 
| 127 | 2 |  |  |  |  | 5 | 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 |  |  |  |  | 11 | m_class    => (%{ $rel->{class} })[0], | 
|  | 4 |  |  |  |  | 42 |  | 
| 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 |  |  |  |  | 59 | $self->{"relation_instance_$relname"}; | 
| 151 |  |  |  |  |  |  | } | 
| 152 | 15 |  |  |  |  | 111 | } | 
| 153 | 11 |  |  | 11 |  | 83 | use strict 'refs'; | 
|  | 11 |  |  |  |  | 22 |  | 
|  | 11 |  |  |  |  | 4318 |  | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 141 |  |  |  |  | 391 | $class->auto_save(0); | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 141 |  | 100 |  |  | 461 | return bless $param || {}, $class; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | sub auto_load { | 
| 163 | 8 |  |  | 8 | 1 | 290 | my ($class) = @_; | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 8 |  |  |  |  | 29 | 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 |  |  |  |  | 17 | join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/) | 
|  | 8 |  |  |  |  | 41 |  | 
|  | 10 |  |  |  |  | 48 |  | 
|  | 20 |  |  |  |  | 38 |  | 
| 170 |  |  |  |  |  |  | } $class_name; | 
| 171 | 8 |  |  |  |  | 19 | $table_name .= 's'; | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | # 0. check the name | 
| 174 | 8 |  |  |  |  | 38 | my $table_info_sth = $class->dbh->table_info('', '%', $table_name, 'TABLE'); | 
| 175 | 8 | 50 |  |  |  | 3637 | $table_info_sth->fetchrow_hashref or croak "Can't find table '$table_name' in the database"; | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | # 1. columns list | 
| 178 | 8 |  |  |  |  | 48 | my $column_info_sth = $class->dbh->column_info(undef, undef, $table_name, undef); | 
| 179 | 8 |  |  |  |  | 9683 | my $cols = $column_info_sth->fetchall_arrayref({}); | 
| 180 | 8 |  |  |  |  | 1125 | my @columns = (); | 
| 181 | 8 |  |  |  |  | 35 | push @columns, $_->{COLUMN_NAME} for @$cols; | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # 2. Primary key | 
| 184 | 8 |  |  |  |  | 27 | my $primary_key_sth = $class->dbh->primary_key_info(undef, undef, $table_name); | 
| 185 | 8 |  |  |  |  | 6781 | my $primary_key_data = $primary_key_sth->fetchrow_hashref; | 
| 186 | 8 | 100 |  |  |  | 136 | my $primary_key = ($primary_key_data) ? $primary_key_data->{COLUMN_NAME} : undef; | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # 3. Foreign keys | 
| 189 |  |  |  |  |  |  | # TODO | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 8 | 50 |  |  |  | 64 | $class->table_name($table_name) if $table_name; | 
| 192 | 8 | 100 |  |  |  | 46 | $class->primary_key($primary_key) if $primary_key; | 
| 193 | 8 | 50 |  |  |  | 43 | $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 |  | 257 | my ($class, $fields) = @_; | 
| 203 |  |  |  |  |  |  |  | 
| 204 | 141 |  |  |  |  | 228 | my $super = caller; | 
| 205 | 141 | 50 |  |  |  | 254 | return unless $fields; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 11 |  |  | 11 |  | 78 | no strict 'refs'; | 
|  | 11 |  |  |  |  | 24 |  | 
|  | 11 |  |  |  |  | 1236 |  | 
| 208 |  |  |  |  |  |  | FIELD: | 
| 209 | 141 |  |  |  |  | 231 | for my $f (@$fields) { | 
| 210 | 608 |  |  |  |  | 986 | my $pkg_accessor_name = $class . '::' . $f; | 
| 211 | 608 | 100 |  |  |  | 1763 | next FIELD if $class->can($pkg_accessor_name); | 
| 212 | 50 |  |  |  |  | 152 | *{$pkg_accessor_name} = sub { | 
| 213 | 94 | 100 |  | 94 |  | 240 | if ( scalar @_ > 1 ) { | 
| 214 | 13 |  |  |  |  | 29 | $_[0]->{$f} = $_[1]; | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 13 |  |  |  |  | 28 | return $_[0]; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 | 81 |  |  |  |  | 315 | return $_[0]->{$f}; | 
| 220 |  |  |  |  |  |  | } | 
| 221 | 50 |  |  |  |  | 172 | } | 
| 222 | 11 |  |  | 11 |  | 62 | use strict 'refs'; | 
|  | 11 |  |  |  |  | 24 |  | 
|  | 11 |  |  |  |  | 792 |  | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 141 |  |  |  |  | 222 | return 1; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub _mk_ro_accessors { | 
| 228 | 84 |  |  | 84 |  | 141 | my ($class, $fields) = @_; | 
| 229 |  |  |  |  |  |  |  | 
| 230 | 84 | 50 |  |  |  | 135 | return unless $fields; | 
| 231 | 84 |  |  |  |  | 137 | my $super = caller; | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 11 |  |  | 11 |  | 64 | no strict 'refs'; | 
|  | 11 |  |  |  |  | 24 |  | 
|  | 11 |  |  |  |  | 16890 |  | 
| 234 |  |  |  |  |  |  | FIELD: | 
| 235 | 84 |  |  |  |  | 135 | for my $f (@$fields) { | 
| 236 | 84 |  |  |  |  | 141 | my $pkg_accessor_name = $class . '::' . $f; | 
| 237 | 84 | 100 |  |  |  | 329 | next FIELD if $class->can($pkg_accessor_name); | 
| 238 | 1 |  |  |  |  | 4 | *{$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 | 33415 | my ($class, $dsn, $username, $password, $options) = @_; | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 4 |  |  |  |  | 10 | eval { require DBIx::Connector }; | 
|  | 4 |  |  |  |  | 307 |  | 
| 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 |  |  |  | 42 | } if ! exists $options->{HandleError}; | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 4 | 50 |  |  |  | 17 | if ($@) { | 
| 261 | 4 |  |  |  |  | 34 | $connector = ActiveRecord::Simple::Connect->new($dsn, $username, $password, $options); | 
| 262 | 4 |  |  |  |  | 18 | $connector->db_connect; | 
| 263 |  |  |  |  |  |  | } | 
| 264 |  |  |  |  |  |  | else { | 
| 265 | 0 |  |  |  |  | 0 | $connector = DBIx::Connector->new($dsn, $username, $password, $options); | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 4 |  |  |  |  | 20 | return 1; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub belongs_to { | 
| 272 | 9 |  |  | 9 | 1 | 69 | my ($class, $rel_name, $rel_class, $params) = @_; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 9 |  |  |  |  | 29 | 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 |  |  | 60 | _guess(primary_key => $class); | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | my $foreign_key = $params->{fk} || | 
| 285 |  |  |  |  |  |  | $params->{foreign_key} || | 
| 286 | 9 |  | 33 |  |  | 37 | _guess(foreign_key => $rel_class); | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | $new_relation->{params} = { | 
| 289 | 9 |  |  |  |  | 31 | 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 |  |  |  |  | 34 | return $class->_append_relation($rel_name => $new_relation); | 
| 305 |  |  |  |  |  |  | } | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | sub has_many { | 
| 308 | 11 |  |  | 11 | 1 | 102 | my ($class, $rel_name, $rel_class, $params) = @_; | 
| 309 |  |  |  |  |  |  |  | 
| 310 | 11 |  |  |  |  | 88 | my $new_relation = { | 
| 311 |  |  |  |  |  |  | class => $rel_class, | 
| 312 |  |  |  |  |  |  | type => 'many', | 
| 313 |  |  |  |  |  |  | }; | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 11 |  | 50 |  |  | 61 | $params ||= {}; | 
| 316 |  |  |  |  |  |  | #my ($primary_key, $foreign_key); | 
| 317 |  |  |  |  |  |  | my $primary_key = $params->{pk} || | 
| 318 |  |  |  |  |  |  | $params->{primary_key} || | 
| 319 | 11 |  | 33 |  |  | 68 | _guess(primary_key => $class); | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | my $foreign_key = $params->{fk} || | 
| 322 |  |  |  |  |  |  | $params->{foreign_key} || | 
| 323 | 11 |  | 33 |  |  | 56 | _guess(foreign_key => $class); | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | $new_relation->{params} = { | 
| 326 | 11 |  |  |  |  | 76 | pk => $primary_key, | 
| 327 |  |  |  |  |  |  | fk => $foreign_key, | 
| 328 |  |  |  |  |  |  | }; | 
| 329 |  |  |  |  |  |  |  | 
| 330 | 11 |  |  |  |  | 52 | return $class->_append_relation($rel_name => $new_relation); | 
| 331 |  |  |  |  |  |  | } | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | sub _guess { | 
| 334 | 44 |  |  | 44 |  | 95 | my ($what_key, $class) = @_; | 
| 335 |  |  |  |  |  |  |  | 
| 336 | 44 | 100 |  |  |  | 130 | return 'id' if $what_key eq 'primary_key'; | 
| 337 |  |  |  |  |  |  |  | 
| 338 | 22 |  |  |  |  | 33 | eval { load $class }; | 
|  | 22 |  |  |  |  | 73 |  | 
| 339 |  |  |  |  |  |  |  | 
| 340 | 22 |  |  |  |  | 3787 | my $table_name = $class->_table_name; | 
| 341 | 22 | 50 |  |  |  | 164 | $table_name =~ s/s$// if $what_key eq 'foreign_key'; | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 22 | 50 |  |  |  | 111 | 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 | 22 | my ($class, $rel_name, $rel_class, $params) = @_; | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 2 |  |  |  |  | 10 | my $new_relation = { | 
| 356 |  |  |  |  |  |  | class => $rel_class, | 
| 357 |  |  |  |  |  |  | type => 'only', | 
| 358 |  |  |  |  |  |  | }; | 
| 359 |  |  |  |  |  |  |  | 
| 360 | 2 |  | 50 |  |  | 15 | $params ||= {}; | 
| 361 |  |  |  |  |  |  | #my ($primary_key, $foreign_key); | 
| 362 |  |  |  |  |  |  | my $primary_key = $params->{pk} || | 
| 363 |  |  |  |  |  |  | $params->{primary_key} || | 
| 364 | 2 |  | 33 |  |  | 24 | _guess(primary_key => $class); | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | my $foreign_key = $params->{fk} || | 
| 367 |  |  |  |  |  |  | $params->{foreign_key} || | 
| 368 | 2 |  | 33 |  |  | 16 | _guess(foreign_key => $class); | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | $new_relation->{params} = { | 
| 371 | 2 |  |  |  |  | 12 | 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 |  |  |  |  | 15 | $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 |  | 56 | my ($class, $rel_name, $rel_hashref) = @_; | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 22 | 100 |  |  |  | 120 | if ($class->can('_get_relations')) { | 
| 414 | 7 |  |  |  |  | 19 | my $relations = $class->_get_relations(); | 
| 415 | 7 |  |  |  |  | 19 | $relations->{$rel_name} = $rel_hashref; | 
| 416 | 7 |  |  |  |  | 16 | $class->relations($relations); | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  | else { | 
| 419 | 15 |  |  |  |  | 93 | $class->relations({ $rel_name => $rel_hashref }); | 
| 420 |  |  |  |  |  |  | } | 
| 421 |  |  |  |  |  |  |  | 
| 422 | 22 |  |  |  |  | 76 | return; | 
| 423 |  |  |  |  |  |  | } | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | sub columns { | 
| 426 | 18 |  |  | 18 | 1 | 110 | my ($class, @args) = @_; | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | #return if $class->can('_get_columns'); | 
| 429 |  |  |  |  |  |  |  | 
| 430 | 18 |  |  |  |  | 36 | my $columns = []; | 
| 431 | 18 | 100 |  |  |  | 61 | if (scalar @args == 1) { | 
|  |  | 50 |  |  |  |  |  | 
| 432 | 11 |  |  |  |  | 22 | my $arg = shift @args; | 
| 433 | 11 | 50 | 33 |  |  | 62 | if (ref $arg && ref $arg eq 'ARRAY') { | 
|  |  | 0 | 0 |  |  |  |  | 
| 434 | 11 |  |  |  |  | 21 | $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 |  |  |  |  | 23 | push @$columns, @args; | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  |  | 
| 449 | 18 |  |  |  |  | 49 | $class->_mk_attribute_getter('_get_columns', $columns); | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | sub mixins { | 
| 453 | 1 |  |  | 1 | 1 | 8 | 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 | 88 | my ($class, $primary_key) = @_; | 
| 490 |  |  |  |  |  |  |  | 
| 491 | 15 |  |  |  |  | 45 | $class->_mk_attribute_getter('_get_primary_key', $primary_key); | 
| 492 | 15 | 50 |  |  |  | 97 | $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 | 7233 | my ($class, $table_name) = @_; | 
| 504 |  |  |  |  |  |  |  | 
| 505 | 18 |  |  |  |  | 95 | $class->_mk_attribute_getter('_get_table_name', $table_name); | 
| 506 |  |  |  |  |  |  | } | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | sub _table_name { | 
| 509 | 37 | 100 |  | 37 |  | 113 | my $class = ref $_[0] ? ref $_[0] : $_[0]; | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 37 | 50 |  |  |  | 99 | croak 'Invalid data class' if $class =~ /^ActiveRecord::Simple/; | 
| 512 |  |  |  |  |  |  |  | 
| 513 | 37 | 100 |  |  |  | 197 | 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 |  |  |  |  | 87 | return $table_name; | 
| 519 |  |  |  |  |  |  | } | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | sub auto_save { | 
| 522 | 142 |  |  | 142 | 1 | 265 | my ($class, $is_on) = @_; | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 142 | 100 |  |  |  | 250 | $is_on = 1 if not defined $is_on; | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 142 |  |  |  |  | 276 | $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 |  |  |  |  | 48 | $class->_mk_attribute_getter('_get_relations', $relations); | 
| 538 |  |  |  |  |  |  | } | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | sub _mk_attribute_getter { | 
| 541 | 216 |  |  | 216 |  | 393 | my ($class, $method_name, $return) = @_; | 
| 542 |  |  |  |  |  |  |  | 
| 543 | 216 |  |  |  |  | 388 | my $pkg_method_name = $class . '::' . $method_name; | 
| 544 | 216 | 100 |  |  |  | 968 | if ( !$class->can($pkg_method_name) ) { | 
| 545 | 11 |  |  | 11 |  | 85 | no strict 'refs'; | 
|  | 11 |  |  |  |  | 25 |  | 
|  | 11 |  |  |  |  | 20564 |  | 
| 546 | 82 |  |  | 975 |  | 283 | *{$pkg_method_name} = sub { $return }; | 
|  | 82 |  |  |  |  | 730 |  | 
|  | 975 |  |  |  |  | 2115 |  | 
| 547 |  |  |  |  |  |  | } | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | sub dbh { | 
| 551 | 317 |  |  | 317 | 1 | 22908 | my ($self, $dbh) = @_; | 
| 552 |  |  |  |  |  |  |  | 
| 553 | 317 | 100 |  |  |  | 578 | if ($dbh) { | 
| 554 | 3 | 50 |  |  |  | 12 | if ($connector) { | 
| 555 | 0 |  |  |  |  | 0 | $connector->dbh($dbh); | 
| 556 |  |  |  |  |  |  | } | 
| 557 |  |  |  |  |  |  | else { | 
| 558 | 3 |  |  |  |  | 54 | $connector = ActiveRecord::Simple::Connect->new(); | 
| 559 | 3 |  |  |  |  | 12 | $connector->dbh($dbh); | 
| 560 |  |  |  |  |  |  | } | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  |  | 
| 563 | 317 |  |  |  |  | 758 | return $connector->dbh; | 
| 564 |  |  |  |  |  |  | } | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | sub save { | 
| 567 | 11 |  |  | 11 | 1 | 54 | my ($self) = @_; | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | #return unless $self->dbh; | 
| 570 | 11 | 50 |  |  |  | 34 | 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 |  |  | 45 | and $self->{snapshoot} eq freeze $self->to_hash; | 
|  |  |  | 33 |  |  |  |  | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | croak 'Object is read-only' | 
| 577 | 11 | 50 | 33 |  |  | 35 | if exists $self->{read_only} && $self->{read_only} == 1; | 
| 578 |  |  |  |  |  |  |  | 
| 579 | 11 |  |  |  |  | 20 | my $save_param = {}; | 
| 580 | 11 |  |  |  |  | 24 | my $fields = $self->_get_columns; | 
| 581 |  |  |  |  |  |  |  | 
| 582 | 11 | 100 |  |  |  | 49 | 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 |  |  | 154 | next FIELD if defined $pkey && $field eq $pkey && !$self->{$pkey}; | 
|  |  |  | 100 |  |  |  |  | 
| 587 | 38 | 50 | 33 |  |  | 61 | next FIELD if ref $field && ref $field eq 'HASH'; | 
| 588 | 38 |  |  |  |  | 78 | $save_param->{$field} = $self->{$field}; | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | ### Get additional fields from related objects: | 
| 592 | 11 |  |  |  |  | 47 | for my $field (keys %$self) { | 
| 593 | 44 | 100 |  |  |  | 94 | next unless ref $self->{$field}; | 
| 594 | 3 | 50 |  |  |  | 16 | next unless $self->can('_get_relations'); | 
| 595 | 3 | 100 |  |  |  | 7 | next unless grep { $_ eq $field } keys %{ $self->_get_relations }; | 
|  | 3 |  |  |  |  | 17 |  | 
|  | 3 |  |  |  |  | 8 |  | 
| 596 |  |  |  |  |  |  |  | 
| 597 | 1 | 50 |  |  |  | 4 | my $relation = $self->_get_relations->{$field} or next; | 
| 598 | 1 | 50 | 33 |  |  | 7 | next unless $relation->{type} && $relation->{type} eq 'one'; | 
| 599 |  |  |  |  |  |  |  | 
| 600 | 1 |  |  |  |  | 2 | my $fk = $relation->{params}{fk}; | 
| 601 | 1 |  |  |  |  | 3 | my $pk = $relation->{params}{pk}; | 
| 602 |  |  |  |  |  |  |  | 
| 603 | 1 |  |  |  |  | 4 | $save_param->{$fk} = $self->{$field}->$pk; | 
| 604 |  |  |  |  |  |  | } | 
| 605 |  |  |  |  |  |  |  | 
| 606 | 11 |  |  |  |  | 20 | my $result; | 
| 607 | 11 | 100 |  |  |  | 31 | if ($self->{isin_database}) { | 
| 608 | 4 |  |  |  |  | 34 | $result = $self->_update($save_param); | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  | else { | 
| 611 | 7 |  |  |  |  | 32 | $result = $self->_insert($save_param); | 
| 612 |  |  |  |  |  |  | } | 
| 613 | 11 | 50 |  |  |  | 447 | $self->{need_to_save} = 0 if $result; | 
| 614 | 11 | 50 |  |  |  | 25 | delete $self->{SQL} if $result; | 
| 615 |  |  |  |  |  |  |  | 
| 616 | 11 | 50 |  |  |  | 57 | 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 |  | 18 | my ($self, $param) = @_; | 
| 636 |  |  |  |  |  |  |  | 
| 637 | 7 | 50 | 33 |  |  | 17 | return unless $self->dbh && $param; | 
| 638 |  |  |  |  |  |  |  | 
| 639 | 7 |  |  |  |  | 39 | my $table_name  = $self->_table_name; | 
| 640 | 7 |  |  |  |  | 43 | my @field_names  = grep { defined $param->{$_} } sort keys %$param; | 
|  | 21 |  |  |  |  | 49 |  | 
| 641 | 7 | 50 |  |  |  | 35 | 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 |  |  |  |  | 66 |  | 
| 645 |  |  |  |  |  |  |  | 
| 646 | 7 |  |  |  |  | 17 | my (@bind, @values_list); | 
| 647 | 7 |  |  |  |  | 18 | for (@field_names) { | 
| 648 | 20 | 100 |  |  |  | 54 | 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 |  |  |  |  | 33 | push @bind, $param->{$_}; | 
| 654 |  |  |  |  |  |  | } | 
| 655 |  |  |  |  |  |  | } | 
| 656 | 7 |  |  |  |  | 19 | my $values = join q/, /, @values_list; | 
| 657 | 7 |  |  |  |  | 11 | my $pkey_val; | 
| 658 | 7 |  |  |  |  | 24 | my $sql_stm = qq{ | 
| 659 |  |  |  |  |  |  | INSERT INTO "$table_name" ($field_names_str) | 
| 660 |  |  |  |  |  |  | VALUES ($values) | 
| 661 |  |  |  |  |  |  | }; | 
| 662 |  |  |  |  |  |  |  | 
| 663 | 7 | 50 |  |  |  | 17 | 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 |  |  |  |  | 28 | ); | 
| 681 | 7 |  |  |  |  | 545 | $sth->execute(@bind); | 
| 682 |  |  |  |  |  |  |  | 
| 683 | 7 | 100 | 100 |  |  | 43 | if ( $primary_key && defined $self->{$primary_key} ) { | 
| 684 | 3 |  |  |  |  | 25 | $pkey_val = $self->{$primary_key}; | 
| 685 |  |  |  |  |  |  | } | 
| 686 |  |  |  |  |  |  | else { | 
| 687 | 4 |  |  |  |  | 12 | $pkey_val = $self->dbh->last_insert_id(undef, undef, $table_name, undef); | 
| 688 |  |  |  |  |  |  | } | 
| 689 |  |  |  |  |  |  | } | 
| 690 |  |  |  |  |  |  |  | 
| 691 | 7 | 50 | 66 |  |  | 59 | if (defined $primary_key && $self->can($primary_key) && $pkey_val) { | 
|  |  |  | 66 |  |  |  |  | 
| 692 | 6 |  |  |  |  | 18 | $self->$primary_key($pkey_val); | 
| 693 |  |  |  |  |  |  | } | 
| 694 | 7 |  |  |  |  | 16 | $self->{isin_database} = 1; | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 7 |  |  |  |  | 21 | return $pkey_val; | 
| 697 |  |  |  |  |  |  | } | 
| 698 |  |  |  |  |  |  |  | 
| 699 |  |  |  |  |  |  | sub _update { | 
| 700 | 4 |  |  | 4 |  | 15 | my ($self, $param) = @_; | 
| 701 |  |  |  |  |  |  |  | 
| 702 | 4 | 50 | 33 |  |  | 12 | return unless $self->dbh && $param; | 
| 703 |  |  |  |  |  |  |  | 
| 704 | 4 |  |  |  |  | 24 | my $table_name      = $self->_table_name; | 
| 705 | 4 |  |  |  |  | 33 | my @field_names     = sort keys %$param; | 
| 706 | 4 | 0 |  |  |  | 27 | 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 |  |  |  |  | 11 | my (@set_list, @bind); | 
| 710 | 4 |  |  |  |  | 11 | for (@field_names) { | 
| 711 | 17 | 50 |  |  |  | 44 | if (ref $param->{$_} eq 'SCALAR') { | 
| 712 | 0 |  |  |  |  | 0 | push @set_list, $_ . ' = ' . ${ $param->{$_} }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 713 |  |  |  |  |  |  | } | 
| 714 |  |  |  |  |  |  | else { | 
| 715 | 17 |  |  |  |  | 31 | push @set_list, "$_ = ?"; | 
| 716 | 17 |  |  |  |  | 51 | push @bind, $param->{$_}; | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | } | 
| 719 | 4 |  |  |  |  | 17 | my $setstring = join q/, /, @set_list; | 
| 720 | 4 |  |  |  |  | 11 | 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 |  |  |  |  | 22 | ); | 
| 730 |  |  |  |  |  |  |  | 
| 731 | 4 |  |  |  |  | 13 | return $self->dbh->do($sql_stm, undef, @bind); | 
| 732 |  |  |  |  |  |  | } | 
| 733 |  |  |  |  |  |  |  | 
| 734 |  |  |  |  |  |  | # param: | 
| 735 |  |  |  |  |  |  | #     cascade => 1 | 
| 736 |  |  |  |  |  |  | sub delete { | 
| 737 | 2 |  |  | 2 | 1 | 1003 | my ($self, $param) = @_; | 
| 738 |  |  |  |  |  |  |  | 
| 739 | 2 | 50 |  |  |  | 7 | return unless $self->dbh; | 
| 740 |  |  |  |  |  |  |  | 
| 741 | 2 |  |  |  |  | 11 | my $table_name = $self->_table_name; | 
| 742 | 2 |  |  |  |  | 7 | my $pkey = $self->_get_primary_key; | 
| 743 |  |  |  |  |  |  |  | 
| 744 | 2 | 50 |  |  |  | 9 | return unless $self->{$pkey}; | 
| 745 |  |  |  |  |  |  |  | 
| 746 | 2 |  |  |  |  | 10 | my $sql = qq{ | 
| 747 |  |  |  |  |  |  | DELETE FROM "$table_name" WHERE $pkey = ? | 
| 748 |  |  |  |  |  |  | }; | 
| 749 | 2 | 0 | 33 |  |  | 10 | $sql .= ' CASCADE ' if $param && $param->{cascade}; | 
| 750 |  |  |  |  |  |  |  | 
| 751 | 2 |  |  |  |  | 4 | my $res = undef; | 
| 752 | 2 |  |  |  |  | 8 | $sql = ActiveRecord::Simple::Utils::quote_sql_stmt($sql, $self->dbh->{Driver}{Name}); | 
| 753 |  |  |  |  |  |  |  | 
| 754 | 2 | 50 |  |  |  | 8 | if ( $self->dbh->do($sql, undef, $self->{$pkey}) ) { | 
| 755 | 2 |  |  |  |  | 174 | $self->{isin_database} = undef; | 
| 756 | 2 |  |  |  |  | 4 | delete $self->{$pkey}; | 
| 757 |  |  |  |  |  |  |  | 
| 758 | 2 |  |  |  |  | 5 | $res = 1; | 
| 759 |  |  |  |  |  |  | } | 
| 760 |  |  |  |  |  |  |  | 
| 761 | 2 |  |  |  |  | 9 | 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 | 29 | my ($self, $param) = @_; | 
| 775 |  |  |  |  |  |  |  | 
| 776 | 3 |  |  |  |  | 7 | my $field_names = $self->_get_columns; | 
| 777 | 3 | 100 |  |  |  | 16 | push @$field_names, keys %{ $self->_get_mixins } if $self->can('_get_mixins'); | 
|  | 2 |  |  |  |  | 4 |  | 
| 778 | 3 |  |  |  |  | 6 | my $attrs = {}; | 
| 779 |  |  |  |  |  |  |  | 
| 780 | 3 |  |  |  |  | 6 | for my $field (@$field_names) { | 
| 781 | 15 | 50 |  |  |  | 24 | next if ref $field; | 
| 782 | 15 | 100 | 66 |  |  | 35 | if ( $param && $param->{only_defined_fields} ) { | 
| 783 | 7 | 100 |  |  |  | 13 | $attrs->{$field} = $self->{$field} if defined $self->$field; | 
| 784 |  |  |  |  |  |  | } | 
| 785 |  |  |  |  |  |  | else { | 
| 786 | 8 |  |  |  |  | 16 | $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 | 5202 | sub find   { ActiveRecord::Simple::Find->new(shift, @_) } | 
| 820 | 13 |  |  | 13 | 1 | 87 | 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 | 24 | sub select { ActiveRecord::Simple::Find->select(shift, @_) } | 
| 852 |  |  |  |  |  |  |  | 
| 853 | 7 |  |  | 7 |  | 33 | 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 |  |  |  |  | 4 | my $sub = $AUTOLOAD; $sub =~ s/.*:://g; | 
|  | 2 |  |  |  |  | 13 |  | 
| 862 | 2 |  |  |  |  | 6 | my $error = "Unknown method: $sub"; | 
| 863 |  |  |  |  |  |  |  | 
| 864 | 2 | 50 |  |  |  | 13 | croak "Error while executing '$sub' method, '$self' is not a valid (blessed) object." unless blessed $self; | 
| 865 | 2 | 100 |  |  |  | 136 | croak "Undefined object for method $sub: must be not undef" unless $param; | 
| 866 |  |  |  |  |  |  |  | 
| 867 | 1 | 50 |  |  |  | 11 | croak $error unless $self->can('_get_relations'); | 
| 868 | 1 |  |  |  |  | 2 | my @many2manies; | 
| 869 | 1 |  |  |  |  | 2 | my $relations = $self->_get_relations; | 
| 870 |  |  |  |  |  |  |  | 
| 871 | 1 |  |  |  |  | 2 | 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 |  |  |  | 7 | next if !$subclass->can('_get_relations'); | 
| 877 | 1 |  |  |  |  | 3 | my $relations2 = $subclass->_get_relations; | 
| 878 |  |  |  |  |  |  |  | 
| 879 | 1 |  |  |  |  | 4 | for my $rel_name (keys %$relations2) { | 
| 880 | 2 | 50 |  |  |  | 4 | next unless exists $relations2->{$rel_name}; | 
| 881 |  |  |  |  |  |  |  | 
| 882 | 2 |  |  |  |  | 5 | my $pk = $relations2->{$rel_name}{params}{pk}; | 
| 883 | 2 |  |  |  |  | 3 | my $fk = $relations2->{$rel_name}{params}{fk}; | 
| 884 |  |  |  |  |  |  |  | 
| 885 | 2 | 50 | 33 |  |  | 22 | next unless $pk && $fk; | 
| 886 |  |  |  |  |  |  |  | 
| 887 | 2 | 100 |  |  |  | 8 | $class_options{$fk} = ($rel_name eq $sub) ? $param->$pk : $self->$pk; | 
| 888 |  |  |  |  |  |  | } | 
| 889 |  |  |  |  |  |  | } | 
| 890 |  |  |  |  |  |  |  | 
| 891 | 1 |  |  |  |  | 9 | return $subclass->new(\%class_options); | 
| 892 |  |  |  |  |  |  | } | 
| 893 |  |  |  |  |  |  |  | 
| 894 |  |  |  |  |  |  | ### Private | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | 1; | 
| 897 |  |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | __END__; |