| 1 | # $Id$ |
|---|
| 2 | |
|---|
| 3 | package Data::ObjectDriver::BaseObject; |
|---|
| 4 | use strict; |
|---|
| 5 | use warnings; |
|---|
| 6 | |
|---|
| 7 | use Scalar::Util qw(weaken); |
|---|
| 8 | use Carp (); |
|---|
| 9 | |
|---|
| 10 | use Class::Trigger qw( pre_save post_save post_load pre_search |
|---|
| 11 | pre_insert post_insert pre_update post_update |
|---|
| 12 | pre_remove post_remove ); |
|---|
| 13 | |
|---|
| 14 | sub install_properties { |
|---|
| 15 | my $class = shift; |
|---|
| 16 | no strict 'refs'; |
|---|
| 17 | my($props) = @_; |
|---|
| 18 | *{"${class}::__properties"} = sub { $props }; |
|---|
| 19 | |
|---|
| 20 | # predefine getter/setter methods here |
|---|
| 21 | foreach my $col (@{ $props->{columns} }) { |
|---|
| 22 | # Skip adding this method if the class overloads it. |
|---|
| 23 | # this lets the SUPER::columnname magic do it's thing |
|---|
| 24 | if (!defined (*{"${class}::$col"})) { |
|---|
| 25 | *{"${class}::$col"} = $class->column_func($col); |
|---|
| 26 | } |
|---|
| 27 | } |
|---|
| 28 | $props; |
|---|
| 29 | } |
|---|
| 30 | |
|---|
| 31 | sub properties { |
|---|
| 32 | my $this = shift; |
|---|
| 33 | my $class = ref($this) || $this; |
|---|
| 34 | $class->__properties; |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | # see docs below |
|---|
| 38 | |
|---|
| 39 | sub has_a { |
|---|
| 40 | my $class = shift; |
|---|
| 41 | my @args = @_; |
|---|
| 42 | |
|---|
| 43 | # Iterate over each remote object |
|---|
| 44 | foreach my $config (@args) { |
|---|
| 45 | my $parentclass = $config->{class}; |
|---|
| 46 | |
|---|
| 47 | # Parameters |
|---|
| 48 | my $column = $config->{column}; |
|---|
| 49 | my $method = $config->{method}; |
|---|
| 50 | my $cached = $config->{cached} || 0; |
|---|
| 51 | my $parent_method = $config->{parent_method}; |
|---|
| 52 | |
|---|
| 53 | # column is required |
|---|
| 54 | if (!defined($column)) { |
|---|
| 55 | die "Please specify a valid column for $parentclass" |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | # create a method name based on the column |
|---|
| 59 | if (! defined $method) { |
|---|
| 60 | if (!ref($column)) { |
|---|
| 61 | $method = $column; |
|---|
| 62 | $method =~ s/_id$//; |
|---|
| 63 | $method .= "_obj"; |
|---|
| 64 | } elsif (ref($column) eq 'ARRAY') { |
|---|
| 65 | foreach my $col (@{$column}) { |
|---|
| 66 | $col =~ s/_id$//; |
|---|
| 67 | $method .= $col . '_'; |
|---|
| 68 | } |
|---|
| 69 | $method .= "obj"; |
|---|
| 70 | } |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | # die if we have clashing methods method |
|---|
| 74 | if (! defined $method || defined(*{"${class}::$method"})) { |
|---|
| 75 | die "Please define a valid method for $class->$column"; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | if ($cached) { |
|---|
| 79 | # Store cached item inside this object's namespace |
|---|
| 80 | my $cachekey = "__cache_$method"; |
|---|
| 81 | |
|---|
| 82 | no strict 'refs'; |
|---|
| 83 | *{"${class}::$method"} = sub { |
|---|
| 84 | my $obj = shift; |
|---|
| 85 | |
|---|
| 86 | return $obj->{$cachekey} |
|---|
| 87 | if defined $obj->{$cachekey}; |
|---|
| 88 | |
|---|
| 89 | my $id = (ref($column) eq 'ARRAY') |
|---|
| 90 | ? [ map { $obj->{column_values}->{$_} } @{$column}] |
|---|
| 91 | : $obj->{column_values}->{$column} |
|---|
| 92 | ; |
|---|
| 93 | ## Hold in a variable here too, so we don't lose it immediately |
|---|
| 94 | ## by having only the weak reference. |
|---|
| 95 | my $ret = $obj->{$cachekey} = $parentclass->lookup($id); |
|---|
| 96 | weaken $obj->{$cachekey}; |
|---|
| 97 | return $ret; |
|---|
| 98 | }; |
|---|
| 99 | } else { |
|---|
| 100 | if (ref($column)) { |
|---|
| 101 | no strict 'refs'; |
|---|
| 102 | *{"${class}::$method"} = sub { |
|---|
| 103 | my $obj = shift; |
|---|
| 104 | return $parentclass->lookup([ map{ $obj->{column_values}->{$_} } @{$column}]); |
|---|
| 105 | }; |
|---|
| 106 | } else { |
|---|
| 107 | no strict 'refs'; |
|---|
| 108 | *{"${class}::$method"} = sub { |
|---|
| 109 | return $parentclass->lookup(shift()->{column_values}->{$column}); |
|---|
| 110 | }; |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | # now add to the parent |
|---|
| 115 | if (!defined $parent_method) { |
|---|
| 116 | $parent_method = lc($class); |
|---|
| 117 | $parent_method =~ s/^.*:://; |
|---|
| 118 | |
|---|
| 119 | $parent_method .= '_objs'; |
|---|
| 120 | } |
|---|
| 121 | if (ref($column)) { |
|---|
| 122 | no strict 'refs'; |
|---|
| 123 | *{"${parentclass}::$parent_method"} = sub { |
|---|
| 124 | my $obj = shift; |
|---|
| 125 | my $terms = shift || {}; |
|---|
| 126 | my $args = shift; |
|---|
| 127 | |
|---|
| 128 | my $primary_key_tuple = $obj->primary_key_tuple; |
|---|
| 129 | my $primary_key = $obj->primary_key; |
|---|
| 130 | |
|---|
| 131 | # inject pk search into given terms. |
|---|
| 132 | # composite key, ugh |
|---|
| 133 | foreach my $key (@{$primary_key_tuple}) { |
|---|
| 134 | $terms->{$key} = shift(@{$primary_key}); |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | return $class->search($terms, $args); |
|---|
| 138 | }; |
|---|
| 139 | } else { |
|---|
| 140 | no strict 'refs'; |
|---|
| 141 | *{"${parentclass}::$parent_method"} = sub { |
|---|
| 142 | my $obj = shift; |
|---|
| 143 | my $terms = shift || {}; |
|---|
| 144 | my $args = shift; |
|---|
| 145 | # TBD - use primary_key_to_terms |
|---|
| 146 | $terms->{$column} = $obj->primary_key; |
|---|
| 147 | return $class->search($terms, $args); |
|---|
| 148 | }; |
|---|
| 149 | }; |
|---|
| 150 | } # end of loop over class names |
|---|
| 151 | return; |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | sub driver { |
|---|
| 155 | my $class = shift; |
|---|
| 156 | $class->properties->{driver} ||= $class->properties->{get_driver}->(); |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | sub get_driver { |
|---|
| 160 | my $class = shift; |
|---|
| 161 | $class->properties->{get_driver} = shift if @_; |
|---|
| 162 | } |
|---|
| 163 | |
|---|
| 164 | sub new { bless {}, shift } |
|---|
| 165 | |
|---|
| 166 | sub is_pkless { |
|---|
| 167 | my $obj = shift; |
|---|
| 168 | my $prop_pk = $obj->properties->{primary_key}; |
|---|
| 169 | return 1 if ! $prop_pk; |
|---|
| 170 | return 1 if ref $prop_pk eq 'ARRAY' && ! @$prop_pk; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | sub is_primary_key { |
|---|
| 174 | my $obj = shift; |
|---|
| 175 | my($col) = @_; |
|---|
| 176 | |
|---|
| 177 | my $prop_pk = $obj->properties->{primary_key}; |
|---|
| 178 | if (ref($prop_pk)) { |
|---|
| 179 | for my $pk (@$prop_pk) { |
|---|
| 180 | return 1 if $pk eq $col; |
|---|
| 181 | } |
|---|
| 182 | } else { |
|---|
| 183 | return 1 if $prop_pk eq $col; |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | return; |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | sub primary_key_tuple { |
|---|
| 190 | my $obj = shift; |
|---|
| 191 | my $pk = $obj->properties->{primary_key}; |
|---|
| 192 | $pk = [ $pk ] unless ref($pk) eq 'ARRAY'; |
|---|
| 193 | $pk; |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | sub primary_key { |
|---|
| 197 | my $obj = shift; |
|---|
| 198 | my $pk = $obj->primary_key_tuple; |
|---|
| 199 | my @val = map { $obj->$_() } @$pk; |
|---|
| 200 | @val == 1 ? $val[0] : \@val; |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | sub is_same_array { |
|---|
| 204 | my($a1, $a2) = @_; |
|---|
| 205 | return if ($#$a1 != $#$a2); |
|---|
| 206 | for (my $i = 0; $i <= $#$a1; $i++) { |
|---|
| 207 | return if $a1->[$i] ne $a2->[$i]; |
|---|
| 208 | } |
|---|
| 209 | return 1; |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | sub primary_key_to_terms { |
|---|
| 213 | my($obj, $id) = @_; |
|---|
| 214 | my $pk = $obj->primary_key_tuple; |
|---|
| 215 | if (! defined $id) { |
|---|
| 216 | $id = $obj->primary_key; |
|---|
| 217 | } else { |
|---|
| 218 | if (ref($id) eq 'HASH') { |
|---|
| 219 | my @keys = sort keys %$id; |
|---|
| 220 | unless (is_same_array(\@keys, [ sort @$pk ])) { |
|---|
| 221 | Carp::croak("keys don't match with primary keys: @keys"); |
|---|
| 222 | } |
|---|
| 223 | return $id; |
|---|
| 224 | } |
|---|
| 225 | } |
|---|
| 226 | $id = [ $id ] unless ref($id) eq 'ARRAY'; |
|---|
| 227 | my $i = 0; |
|---|
| 228 | my %terms; |
|---|
| 229 | @terms{@$pk} = @$id; |
|---|
| 230 | \%terms; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | sub has_primary_key { |
|---|
| 234 | my $obj = shift; |
|---|
| 235 | return unless @{$obj->primary_key_tuple}; |
|---|
| 236 | my $val = $obj->primary_key; |
|---|
| 237 | $val = [ $val ] unless ref($val) eq 'ARRAY'; |
|---|
| 238 | for my $v (@$val) { |
|---|
| 239 | return unless defined $v; |
|---|
| 240 | } |
|---|
| 241 | 1; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | sub datasource { $_[0]->properties->{datasource} } |
|---|
| 245 | |
|---|
| 246 | sub columns_of_type { |
|---|
| 247 | my $obj = shift; |
|---|
| 248 | my($type) = @_; |
|---|
| 249 | my $props = $obj->properties; |
|---|
| 250 | my $cols = $props->{columns}; |
|---|
| 251 | my $col_defs = $props->{column_defs}; |
|---|
| 252 | my @cols; |
|---|
| 253 | for my $col (@$cols) { |
|---|
| 254 | push @cols, $col if $col_defs->{$col} && $col_defs->{$col} eq $type; |
|---|
| 255 | } |
|---|
| 256 | \@cols; |
|---|
| 257 | } |
|---|
| 258 | |
|---|
| 259 | sub set_values { |
|---|
| 260 | my $obj = shift; |
|---|
| 261 | my $values = shift; |
|---|
| 262 | for my $col (keys %$values) { |
|---|
| 263 | unless ( $obj->has_column($col) ) { |
|---|
| 264 | Carp::croak("You tried to set inexistent column $col to value $values->{$col} on " . ref($obj)); |
|---|
| 265 | } |
|---|
| 266 | $obj->$col($values->{$col}); |
|---|
| 267 | } |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | sub set_values_internal { |
|---|
| 271 | my $obj = shift; |
|---|
| 272 | my $values = shift; |
|---|
| 273 | for my $col (keys %$values) { |
|---|
| 274 | # Not needed for the internal version of this method |
|---|
| 275 | #unless ( $obj->has_column($col) ) { |
|---|
| 276 | # Carp::croak("You tried to set inexistent column $col to value $values->{$col} on " . ref($obj)); |
|---|
| 277 | #} |
|---|
| 278 | |
|---|
| 279 | $obj->column_values->{$col} = $values->{$col}; |
|---|
| 280 | } |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | sub clone { |
|---|
| 284 | my $obj = shift; |
|---|
| 285 | my $clone = $obj->clone_all; |
|---|
| 286 | for my $pk (@{ $obj->primary_key_tuple }) { |
|---|
| 287 | $clone->$pk(undef); |
|---|
| 288 | } |
|---|
| 289 | $clone; |
|---|
| 290 | } |
|---|
| 291 | |
|---|
| 292 | sub clone_all { |
|---|
| 293 | my $obj = shift; |
|---|
| 294 | my $clone = ref($obj)->new(); |
|---|
| 295 | $clone->set_values_internal($obj->column_values); |
|---|
| 296 | $clone->{changed_cols} = $obj->{changed_cols}; |
|---|
| 297 | $clone; |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | sub has_column { |
|---|
| 301 | my $obj = shift; |
|---|
| 302 | my($col) = @_; |
|---|
| 303 | $obj->{__col_names} ||= { map { $_ => 1 } @{ $obj->column_names } }; |
|---|
| 304 | exists $obj->{__col_names}->{$col}; |
|---|
| 305 | } |
|---|
| 306 | |
|---|
| 307 | sub column_names { |
|---|
| 308 | ## Reference to a copy. |
|---|
| 309 | [ @{ shift->properties->{columns} } ] |
|---|
| 310 | } |
|---|
| 311 | |
|---|
| 312 | sub column_values { $_[0]->{'column_values'} ||= {} } |
|---|
| 313 | |
|---|
| 314 | ## In 0.1 version we didn't die on inexistent column |
|---|
| 315 | ## which might lead to silent bugs |
|---|
| 316 | ## You should override column if you want to find the old |
|---|
| 317 | ## behaviour |
|---|
| 318 | sub column { |
|---|
| 319 | my $obj = shift; |
|---|
| 320 | my $col = shift or return; |
|---|
| 321 | unless ($obj->has_column($col)) { |
|---|
| 322 | Carp::croak("Cannot find column '$col' for class '" . ref($obj) . "'"); |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | # set some values |
|---|
| 326 | if (@_) { |
|---|
| 327 | $obj->{column_values}->{$col} = shift; |
|---|
| 328 | unless ($_[0] && ref($_[0]) eq 'HASH' && $_[0]->{no_changed_flag}) { |
|---|
| 329 | $obj->{changed_cols}->{$col}++; |
|---|
| 330 | } |
|---|
| 331 | } |
|---|
| 332 | |
|---|
| 333 | $obj->{column_values}->{$col}; |
|---|
| 334 | } |
|---|
| 335 | |
|---|
| 336 | sub column_func { |
|---|
| 337 | my $obj = shift; |
|---|
| 338 | my $col = shift or die "Must specify column"; |
|---|
| 339 | |
|---|
| 340 | return sub { |
|---|
| 341 | my $obj = shift; |
|---|
| 342 | # getter |
|---|
| 343 | return $obj->{column_values}->{$col} unless (@_); |
|---|
| 344 | |
|---|
| 345 | # setter |
|---|
| 346 | my ($val, $flags) = @_; |
|---|
| 347 | $obj->{column_values}->{$col} = $val; |
|---|
| 348 | unless (($val && ref($val) eq 'HASH' && $val->{no_changed_flag}) || |
|---|
| 349 | $flags->{no_changed_flag}) { |
|---|
| 350 | $obj->{changed_cols}->{$col}++; |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | return $obj->{column_values}->{$col}; |
|---|
| 354 | }; |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | |
|---|
| 358 | sub changed_cols_and_pk { |
|---|
| 359 | my $obj = shift; |
|---|
| 360 | keys %{$obj->{changed_cols}}; |
|---|
| 361 | } |
|---|
| 362 | |
|---|
| 363 | sub changed_cols { |
|---|
| 364 | my $obj = shift; |
|---|
| 365 | my $pk = $obj->primary_key_tuple; |
|---|
| 366 | my %pk = map { $_ => 1 } @$pk; |
|---|
| 367 | grep !$pk{$_}, $obj->changed_cols_and_pk; |
|---|
| 368 | } |
|---|
| 369 | |
|---|
| 370 | sub is_changed { |
|---|
| 371 | my $obj = shift; |
|---|
| 372 | if (@_) { |
|---|
| 373 | return exists $obj->{changed_cols}->{$_[0]}; |
|---|
| 374 | } else { |
|---|
| 375 | return $obj->changed_cols > 0; |
|---|
| 376 | } |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | sub exists { |
|---|
| 380 | my $obj = shift; |
|---|
| 381 | return 0 unless $obj->has_primary_key; |
|---|
| 382 | $obj->_proxy('exists', @_); |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | sub save { |
|---|
| 386 | my $obj = shift; |
|---|
| 387 | if ($obj->exists) { |
|---|
| 388 | return $obj->update; |
|---|
| 389 | } else { |
|---|
| 390 | return $obj->insert; |
|---|
| 391 | } |
|---|
| 392 | } |
|---|
| 393 | |
|---|
| 394 | sub lookup { |
|---|
| 395 | my $class = shift; |
|---|
| 396 | my $driver = $class->driver; |
|---|
| 397 | my $obj = $driver->lookup($class, @_) or return; |
|---|
| 398 | $driver->cache_object($obj); |
|---|
| 399 | $obj; |
|---|
| 400 | } |
|---|
| 401 | |
|---|
| 402 | sub lookup_multi { |
|---|
| 403 | my $class = shift; |
|---|
| 404 | my $driver = $class->driver; |
|---|
| 405 | my $objs = $driver->lookup_multi($class, @_) or return; |
|---|
| 406 | for my $obj (@$objs) { |
|---|
| 407 | $driver->cache_object($obj) if $obj; |
|---|
| 408 | } |
|---|
| 409 | $objs; |
|---|
| 410 | } |
|---|
| 411 | |
|---|
| 412 | sub search { |
|---|
| 413 | my $class = shift; |
|---|
| 414 | my($terms, $args) = @_; |
|---|
| 415 | my $driver = $class->driver; |
|---|
| 416 | my @objs = $driver->search($class, $terms, $args); |
|---|
| 417 | |
|---|
| 418 | ## Don't attempt to cache objects where the caller specified fetchonly, |
|---|
| 419 | ## because they won't be complete. |
|---|
| 420 | ## Also skip this step if we don't get any objects back from the search |
|---|
| 421 | if (!$args->{fetchonly} || !@objs) { |
|---|
| 422 | for my $obj (@objs) { |
|---|
| 423 | $driver->cache_object($obj) if $obj; |
|---|
| 424 | } |
|---|
| 425 | } |
|---|
| 426 | $driver->list_or_iterator(\@objs); |
|---|
| 427 | } |
|---|
| 428 | |
|---|
| 429 | sub remove { shift->_proxy('remove', @_) } |
|---|
| 430 | sub update { shift->_proxy('update', @_) } |
|---|
| 431 | sub insert { shift->_proxy('insert', @_) } |
|---|
| 432 | sub fetch_data { shift->_proxy('fetch_data', @_) } |
|---|
| 433 | |
|---|
| 434 | sub refresh { |
|---|
| 435 | my $obj = shift; |
|---|
| 436 | return unless $obj->has_primary_key; |
|---|
| 437 | my $fields = $obj->fetch_data; |
|---|
| 438 | $obj->set_values_internal($fields); |
|---|
| 439 | # XXX not sure this is the right place |
|---|
| 440 | $obj->call_trigger('post_load'); |
|---|
| 441 | return 1; |
|---|
| 442 | } |
|---|
| 443 | |
|---|
| 444 | sub _proxy { |
|---|
| 445 | my $obj = shift; |
|---|
| 446 | my($meth, @args) = @_; |
|---|
| 447 | $obj->driver->$meth($obj, @args); |
|---|
| 448 | } |
|---|
| 449 | |
|---|
| 450 | sub deflate { { columns => shift->column_values } } |
|---|
| 451 | |
|---|
| 452 | sub inflate { |
|---|
| 453 | my $class = shift; |
|---|
| 454 | my($deflated) = @_; |
|---|
| 455 | my $obj = $class->new; |
|---|
| 456 | $obj->set_values_internal($deflated->{columns}); |
|---|
| 457 | $obj->{changed_cols} = {}; |
|---|
| 458 | $obj; |
|---|
| 459 | } |
|---|
| 460 | |
|---|
| 461 | sub DESTROY { } |
|---|
| 462 | |
|---|
| 463 | sub AUTOLOAD { |
|---|
| 464 | my $obj = $_[0]; |
|---|
| 465 | (my $col = our $AUTOLOAD) =~ s!.+::!!; |
|---|
| 466 | no strict 'refs'; |
|---|
| 467 | Carp::croak("Cannot find method '$col' for class '$obj'") unless ref $obj; |
|---|
| 468 | unless ($obj->has_column($col)) { |
|---|
| 469 | Carp::croak("Cannot find column '$col' for class '" . ref($obj) . "'"); |
|---|
| 470 | } |
|---|
| 471 | |
|---|
| 472 | *$AUTOLOAD = $obj->column_func($col); |
|---|
| 473 | |
|---|
| 474 | goto &$AUTOLOAD; |
|---|
| 475 | } |
|---|
| 476 | |
|---|
| 477 | 1; |
|---|
| 478 | __END__ |
|---|
| 479 | |
|---|
| 480 | =head1 NAME |
|---|
| 481 | |
|---|
| 482 | Data::ObjectDriver::BaseObject - base class for modeled objects |
|---|
| 483 | |
|---|
| 484 | =head1 SYNOPSIS |
|---|
| 485 | |
|---|
| 486 | See synopsis in I<Data::ObjectDriver>. |
|---|
| 487 | |
|---|
| 488 | =head1 DESCRIPTION |
|---|
| 489 | |
|---|
| 490 | I<Data::ObjectDriver::BaseObject> provides services to data objects modeled |
|---|
| 491 | with the I<Data::ObjectDriver> object relational mapper. |
|---|
| 492 | |
|---|
| 493 | =head1 USAGE |
|---|
| 494 | |
|---|
| 495 | =head2 Class->install_properties({ ... }) |
|---|
| 496 | |
|---|
| 497 | Sets up columns, indexes, primary keys, etc. |
|---|
| 498 | |
|---|
| 499 | =head2 Class->properties |
|---|
| 500 | |
|---|
| 501 | Returns the list of properties. |
|---|
| 502 | |
|---|
| 503 | =head2 Class->has_a(ParentClass => { ... }, ParentClass2 => { ...} ) |
|---|
| 504 | |
|---|
| 505 | Creates utility methods that map this object to parent Data::ObjectDriver objects. |
|---|
| 506 | |
|---|
| 507 | Pass in a list of parent classes to map with a hash of parameters. The following parameters |
|---|
| 508 | are recognized: |
|---|
| 509 | |
|---|
| 510 | =over 4 |
|---|
| 511 | |
|---|
| 512 | =item * column |
|---|
| 513 | |
|---|
| 514 | Name of the column(s) in this class to map with. Pass in a single string if |
|---|
| 515 | the column is a singular key, an array ref if this is a composite key. |
|---|
| 516 | |
|---|
| 517 | column => 'user_id' |
|---|
| 518 | column => ['user_id', 'photo_id'] |
|---|
| 519 | |
|---|
| 520 | =item * method [OPTIONAL] |
|---|
| 521 | |
|---|
| 522 | Name of the method to create in this class. Defaults to the column name(s) without |
|---|
| 523 | the _id suffix and with the suffix _obj appended. |
|---|
| 524 | |
|---|
| 525 | =item * parent_method [OPTIONAL] |
|---|
| 526 | |
|---|
| 527 | Name of the method created in the parent class. Default is the lowercased |
|---|
| 528 | name of the current class with the suffix _objs. |
|---|
| 529 | |
|---|
| 530 | =item * cached [OPTIONAL] |
|---|
| 531 | |
|---|
| 532 | If set to 1 cache the result of the fetching the parent object in the current class. Note |
|---|
| 533 | that this is a private copy to this class only, and does not interact with other caches |
|---|
| 534 | in the system. |
|---|
| 535 | |
|---|
| 536 | =back |
|---|
| 537 | |
|---|
| 538 | =head2 column_func |
|---|
| 539 | |
|---|
| 540 | This method is called to get/set column values. Subclasses can override this and get different |
|---|
| 541 | behavior. |
|---|
| 542 | |
|---|
| 543 | =head2 Class->driver |
|---|
| 544 | |
|---|
| 545 | Returns the database driver for this class, invoking the class's I<get_driver> |
|---|
| 546 | function if necessary. |
|---|
| 547 | |
|---|
| 548 | =head2 Class->get_driver($driver) |
|---|
| 549 | |
|---|
| 550 | Sets the function used to find the object driver for I<Class> objects. |
|---|
| 551 | |
|---|
| 552 | =head2 $obj->primary_key |
|---|
| 553 | |
|---|
| 554 | Returns the B<values> of the primary key fields of I<$obj>. |
|---|
| 555 | |
|---|
| 556 | =head2 Class->primary_key_tuple |
|---|
| 557 | |
|---|
| 558 | Returns the B<names> of the primary key fields for objects of class I<Class>. |
|---|
| 559 | |
|---|
| 560 | =head2 $obj->has_primary_key |
|---|
| 561 | |
|---|
| 562 | =head2 $obj->clone |
|---|
| 563 | |
|---|
| 564 | Returns a new object of the same class as I<$obj> containing the same data, |
|---|
| 565 | except for primary keys, which are set to C<undef>. |
|---|
| 566 | |
|---|
| 567 | =head2 $obj->clone_all |
|---|
| 568 | |
|---|
| 569 | Returns a new object of the same class as I<$obj> containing the same data, |
|---|
| 570 | including all key fields. |
|---|
| 571 | |
|---|
| 572 | =head2 $obj->deflate |
|---|
| 573 | |
|---|
| 574 | Returns a minimal representation of the object, for use in caches where |
|---|
| 575 | you might want to preserve space (like memcached). Can also be overridden |
|---|
| 576 | by subclasses to store the optimal representation of an object in the |
|---|
| 577 | cache. For example, if you have metadata attached to an object, you might |
|---|
| 578 | want to store that in the cache, as well. |
|---|
| 579 | |
|---|
| 580 | =head2 $class->inflate($deflated) |
|---|
| 581 | |
|---|
| 582 | Inflates the deflated representation of the object I<$deflated> into a |
|---|
| 583 | proper object in the class I<$class>. |
|---|
| 584 | |
|---|
| 585 | =cut |
|---|