File Coverage

blib/lib/Rose/DB/Object/Helpers.pm
Criterion Covered Total %
statement 36 377 9.5
branch 0 204 0.0
condition 0 68 0.0
subroutine 12 50 24.0
pod 29 33 87.8
total 77 732 10.5


line stmt bran cond sub pod time code
1             package Rose::DB::Object::Helpers;
2              
3 63     63   436 use strict;
  63         146  
  63         2299  
4              
5 63     63   26117 use Rose::DB::Object::Constants qw(:all);
  63         180  
  63         12333  
6              
7 63     63   31207 use Rose::Object::MixIn;
  63         410677  
  63         334  
8             our @ISA = qw(Rose::Object::MixIn);
9              
10             require Rose::DB::Object::Util;
11              
12 63     63   4106 use Carp;
  63         162  
  63         8138  
13              
14             our $VERSION = '0.812';
15              
16             __PACKAGE__->export_tags
17             (
18             all =>
19             [
20             qw(clone clone_and_reset load_or_insert load_or_save insert_or_update
21             insert_or_update_on_duplicate_key load_speculative
22             column_value_pairs column_accessor_value_pairs
23             column_mutator_value_pairs
24             column_values_as_yaml column_values_as_json
25             traverse_depth_first as_tree init_with_tree new_from_tree
26             init_with_deflated_tree new_from_deflated_tree
27             as_yaml new_from_yaml init_with_yaml
28             as_json new_from_json init_with_json
29             init_with_column_value_pairs
30             has_loaded_related strip forget_related
31             dirty_columns)
32             ],
33              
34             # This exists for the benefit of the test suite
35             all_noprereq =>
36             [
37             qw(clone clone_and_reset load_or_insert load_or_save insert_or_update
38             insert_or_update_on_duplicate_key load_speculative
39             column_value_pairs column_accessor_value_pairs
40             column_mutator_value_pairs
41             traverse_depth_first as_tree init_with_tree new_from_tree
42             init_with_deflated_tree new_from_deflated_tree
43             init_with_column_value_pairs
44             has_loaded_related strip forget_related
45             dirty_columns)
46             ],
47             );
48              
49             #
50             # Class data
51             #
52              
53             use Rose::Class::MakeMethods::Generic
54             (
55 63         641 inheritable_scalar =>
56             [
57             '_json_object'
58             ],
59 63     63   3170 );
  63         26151  
60              
61             #
62             # Class methods
63             #
64              
65             sub json_encoder
66             {
67 0     0 0   my($class) = shift;
68              
69 0           my $json = $class->_json_object;
70              
71 0 0         unless(defined $json)
72             {
73 0           $json = $class->init_json_encoder;
74             }
75              
76 0           return $json;
77             }
78              
79             sub init_json_encoder
80             {
81 0     0 0   require JSON;
82              
83 0 0         croak "JSON version 2.00 or later is required. You have $JSON::VERSION"
84             unless($JSON::VERSION >= 2.00);
85              
86 0           return JSON->new->utf8->space_after;
87             }
88              
89             *json_decoder = \&json_encoder;
90              
91             #
92             # Object methods
93             #
94              
95 0     0 1   sub load_speculative { shift->load(@_, speculative => 1) }
96              
97             sub load_or_insert
98             {
99 0     0 1   my($self) = shift;
100              
101 0           my($ret, @ret, $loaded, $error);
102              
103             TRY:
104             {
105 0           local $@;
  0            
106              
107             # Ignore any errors due to missing primary/unique keys
108             $loaded = eval
109 0           {
110 0 0         if(wantarray)
111             {
112 0           @ret = $self->load(@_, speculative => 1);
113 0 0         return $ret[0] if($ret[0]); # return from eval
114             }
115             else
116             {
117 0           $ret = $self->load(@_, speculative => 1);
118 0 0         return $ret if($ret); # return from eval
119             }
120              
121 0           return 0; # return from eval
122             };
123              
124 0           $error = $@;
125             }
126              
127 0 0         if($error)
128             {
129             # ...but re-throw all other errors
130 0 0 0       unless(UNIVERSAL::isa($error, 'Rose::DB::Object::Exception') &&
131             $error->code == EXCEPTION_CODE_NO_KEY)
132             {
133 0           $self->meta->handle_error($self);
134 0           return 0;
135             }
136             }
137              
138 0 0         return wantarray ? @ret : $ret if($loaded);
    0          
139              
140 0           return $self->insert;
141             }
142              
143             sub load_or_save
144             {
145 0     0 1   my($self) = shift;
146              
147 0           my($ret, @ret, $loaded, $error);
148              
149             TRY:
150             {
151 0           local $@;
  0            
152              
153             # Ignore any errors due to missing primary/unique keys
154             $loaded = eval
155 0           {
156 0 0         if(wantarray)
157             {
158 0           @ret = $self->load(@_, speculative => 1);
159 0 0         return $ret[0] if($ret[0]); # return from eval
160             }
161             else
162             {
163 0           $ret = $self->load(@_, speculative => 1);
164 0 0         return $ret if($ret); # return from eval
165             }
166              
167 0           return 0; # return from eval
168             };
169              
170 0           $error = $@;
171             }
172              
173 0 0         if($error)
174             {
175             # ...but re-throw all other errors
176 0 0 0       unless(UNIVERSAL::isa($error, 'Rose::DB::Object::Exception') &&
177             $error->code == EXCEPTION_CODE_NO_KEY)
178             {
179 0           $self->meta->handle_error($self);
180 0           return 0;
181             }
182             }
183              
184 0 0         return wantarray ? @ret : $ret if($loaded);
    0          
185              
186 0           return $self->save;
187             }
188              
189              
190             sub insert_or_update
191             {
192 0     0 1   my($self) = shift;
193              
194             # Initially trust the metadata
195 0 0         if($self->{STATE_IN_DB()})
196             {
197 0           local $@;
198 0           eval { $self->save(@_, update => 1) };
  0            
199 0 0 0       return $self || 1 unless($@);
200             }
201              
202 0           my $meta = $self->meta;
203              
204             # This is more "correct"
205             #my $clone = clone($self);
206              
207             # ...but this is a lot faster
208 0           my $clone = bless { %$self }, ref($self);
209              
210 0           my($loaded, $error);
211              
212             TRY:
213             {
214 0           local $@;
  0            
215              
216             # Ignore any errors due to missing primary/unique keys
217 0           eval { $loaded = $clone->load(speculative => 1) };
  0            
218              
219 0           $error = $@;
220             }
221              
222 0 0         if($error)
223             {
224             # ...but re-throw all other errors
225 0 0 0       unless(UNIVERSAL::isa($error, 'Rose::DB::Object::Exception') &&
226             $error->code == EXCEPTION_CODE_NO_KEY)
227             {
228 0           $meta->handle_error($self);
229 0           return 0;
230             }
231             }
232              
233 0 0         if($loaded)
234             {
235             # The long way...
236 0           my %pk;
237             @pk{$meta->primary_key_column_mutator_names} =
238 0           map { $clone->$_() } $meta->primary_key_column_accessor_names;
  0            
239 0           $self->init(%pk);
240              
241             # The short (but dirty) way
242             #my @pk_keys = $meta->primary_key_column_db_value_hash_keys;
243             #@$self{@pk_keys} = @$clone{@pk_keys};
244              
245 0           return $self->save(@_, update => 1);
246             }
247              
248 0           return $self->save(@_, insert => 1)
249             }
250              
251             sub insert_or_update_on_duplicate_key
252             {
253 0     0 1   my($self) = shift;
254              
255 0 0         unless($self->db->supports_on_duplicate_key_update)
256             {
257 0           return insert_or_update($self, @_);
258             }
259              
260 0           return $self->save(@_, insert => 1, on_duplicate_key_update => 1);
261             }
262              
263             __PACKAGE__->pre_import_hook(column_values_as_yaml => sub { require YAML::Syck });
264              
265             sub column_values_as_yaml
266             {
267 0     0 1   local $_[0]->{STATE_SAVING()} = 1;
268 0           YAML::Syck::Dump(scalar Rose::DB::Object::Helpers::column_value_pairs(shift))
269             }
270              
271             __PACKAGE__->pre_import_hook(column_values_as_json => sub { require JSON });
272              
273             sub column_values_as_json
274             {
275 0     0 1   local $_[0]->{STATE_SAVING()} = 1;
276 0           __PACKAGE__->json_encoder->encode(scalar Rose::DB::Object::Helpers::column_value_pairs(shift))
277             }
278              
279             sub init_with_column_value_pairs
280             {
281 0     0 1   my($self) = shift;
282              
283 0 0         my $hash = @_ == 1 ? shift : { @_ };
284 0           my $meta = $self->meta;
285              
286 0           local $self->{STATE_LOADING()} = 1;
287              
288 0           while(my($name, $value) = each(%$hash))
289             {
290 0 0         next unless(length $name);
291 0           my $method = $meta->column($name)->mutator_method_name;
292 0           $self->$method($value);
293             }
294              
295 0           return $self;
296             }
297              
298             sub column_value_pairs
299             {
300 0     0 1   my($self) = shift;
301              
302 0           my %pairs;
303              
304 0           my $methods = $self->meta->column_accessor_method_names_hash;
305              
306 0           while(my($column, $method) = each(%$methods))
307             {
308 0           $pairs{$column} = $self->$method();
309             }
310              
311 0 0         return wantarray ? %pairs : \%pairs;
312             }
313              
314             sub key_column_value_pairs
315             {
316 0     0 0   my($self) = shift;
317              
318 0           my %pairs;
319              
320 0           my $methods = $self->meta->key_column_accessor_method_names_hash;
321              
322 0           while(my($column, $method) = each(%$methods))
323             {
324 0           $pairs{$column} = $self->$method();
325             }
326              
327 0 0         return wantarray ? %pairs : \%pairs;
328             }
329              
330             sub column_accessor_value_pairs
331             {
332 0     0 1   my($self) = shift;
333              
334 0           my %pairs;
335              
336 0           foreach my $method ($self->meta->column_accessor_method_names)
337             {
338 0           $pairs{$method} = $self->$method();
339             }
340              
341 0 0         return wantarray ? %pairs : \%pairs;
342             }
343              
344             sub column_mutator_value_pairs
345             {
346 0     0 1   my($self) = shift;
347              
348 0           my %pairs;
349              
350 0           foreach my $column ($self->meta->columns)
351             {
352 0           my $method = $column->accessor_method_name;
353 0           $pairs{$column->mutator_method_name} = $self->$method();
354             }
355              
356 0 0         return wantarray ? %pairs : \%pairs;
357             }
358              
359             sub clone
360             {
361 0     0 1   my($self) = shift;
362 0           my $class = ref $self;
363 0           local $self->{STATE_CLONING()} = 1;
364 0           my @mutators = $self->meta->column_mutator_method_names;
365 0           my $mutator;
366             return $class->new(map
367             {
368 0 0 0       (defined($mutator = shift(@mutators)) && defined $_) ?
  0            
369             ($mutator => $self->$_()) : ()
370             }
371             $self->meta->column_accessor_method_names);
372             }
373              
374             sub clone_and_reset
375             {
376 0     0 1   my($self) = shift;
377 0           my $class = ref $self;
378 0           local $self->{STATE_CLONING()} = 1;
379 0           my @mutators = $self->meta->column_mutator_method_names;
380 0           my $mutator;
381             my $clone = $class->new(map
382             {
383 0 0 0       (defined($mutator = shift(@mutators)) && defined $_) ?
  0            
384             ($mutator => $self->$_()) : ()
385             }
386             $self->meta->column_accessor_method_names);
387              
388 0           my $meta = $class->meta;
389              
390 63     63   117715 no strict 'refs';
  63         226  
  63         73200  
391              
392             # Blank all primary and unique key columns
393 0           foreach my $method ($meta->primary_key_column_mutator_names)
394             {
395 0           $clone->$method(undef);
396             }
397              
398 0           foreach my $uk ($meta->unique_keys)
399             {
400 0           foreach my $column ($uk->columns)
401             {
402 0           my $method = $meta->column_mutator_method_name($column);
403 0           $clone->$method(undef);
404             }
405             }
406              
407             # Also copy db object, if any
408 0 0         if(my $db = $self->{'db'})
409             {
410             #$self->{FLAG_DB_IS_PRIVATE()} = 0;
411 0           $clone->db($db);
412             }
413              
414 0           return $clone;
415             }
416              
417             sub has_loaded_related
418             {
419 0     0 1   my($self) = shift;
420              
421 0           my $rel; # really a relationship or fk
422              
423 0           my $meta = $self->meta;
424              
425 0 0         if(@_ == 1)
426             {
427 0           my $name = shift;
428              
429 0 0         if($rel = $meta->foreign_key($name))
    0          
430             {
431 0 0         return $rel->object_has_foreign_object($self) ? 1 : 0;
432             }
433             elsif($rel = $meta->relationship($name))
434             {
435 0 0         return $rel->object_has_related_objects($self) ? 1 : 0;
436             }
437             else
438             {
439 0           croak "No foreign key or relationship named '$name' found in ",
440             $meta->class;
441             }
442             }
443             else
444             {
445 0           my %args = @_;
446 0           my $name;
447              
448 0 0         if($name = $args{'foreign_key'})
    0          
449             {
450 0 0         $rel = $meta->foreign_key($name)
451             or croak "No foreign key named '$name' found in ", $meta->class;
452              
453 0 0         return $rel->object_has_foreign_object($self) ? 1 : 0;
454             }
455             elsif($name = $args{'relationship'})
456             {
457 0 0         $rel = $meta->relationship($name)
458             or croak "No relationship named '$name' found in ", $meta->class;
459              
460 0 0         return $rel->object_has_related_objects($self) ? 1 : 0;
461             }
462             else
463             {
464 0           croak "Missing foreign key or relationship name argument";
465             }
466             }
467             }
468              
469             sub forget_related
470             {
471 0     0 1   my($self) = shift;
472              
473 0           my $rel; # really a relationship or fk
474              
475 0           my $meta = $self->meta;
476              
477 0 0         if(@_ == 1)
478             {
479 0           my $name = shift;
480              
481 0 0         if($rel = $meta->foreign_key($name))
    0          
482             {
483 0           return $rel->forget_foreign_object($self);
484             }
485             elsif($rel = $meta->relationship($name))
486             {
487 0           return $rel->forget_related_objects($self);
488             }
489             else
490             {
491 0           croak "No foreign key or relationship named '$name' found in ",
492             $meta->class;
493             }
494             }
495             else
496             {
497 0           my %args = @_;
498 0           my $name;
499              
500 0 0         if($name = $args{'foreign_key'})
    0          
501             {
502 0 0         $rel = $meta->foreign_key($name)
503             or croak "No foreign key named '$name' found in ", $meta->class;
504              
505 0           return $rel->forget_foreign_object($self);
506             }
507             elsif($name = $args{'relationship'})
508             {
509 0 0         $rel = $meta->relationship($name)
510             or croak "No relationship named '$name' found in ", $meta->class;
511              
512 0           return $rel->forget_related_objects($self);
513             }
514             else
515             {
516 0           croak "Missing foreign key or relationship name argument";
517             }
518             }
519             }
520              
521             sub strip
522             {
523 0     0 1   my($self) = shift;
524              
525 0           my %args = @_;
526              
527 0 0 0       my %leave = map { $_ => 1 } (ref $args{'leave'} ? @{$args{'leave'}} : ($args{'leave'} || ''));
  0            
  0            
528              
529 0           my $meta = $self->meta;
530              
531 0 0 0       if($leave{'relationships'} || $leave{'related_objects'})
532             {
533 0           foreach my $rel ($meta->relationships)
534             {
535 0 0         if(my $objs = $rel->object_has_related_objects($self))
536             {
537 0           foreach my $obj (@$objs)
538             {
539 0           Rose::DB::Object::Helpers::strip($obj, @_);
540             }
541             }
542             }
543             }
544             else
545             {
546 0           foreach my $rel ($meta->relationships)
547             {
548 0           delete $self->{$rel->name};
549             }
550             }
551              
552 0 0 0       if($leave{'foreign_keys'} || $leave{'related_objects'})
553             {
554 0           foreach my $rel ($meta->foreign_keys)
555             {
556 0 0         if(my $obj = $rel->object_has_foreign_object($self))
557             {
558 0           Rose::DB::Object::Helpers::strip($obj, @_);
559             }
560             }
561             }
562             else
563             {
564 0           foreach my $fk ($meta->foreign_keys)
565             {
566 0           delete $self->{$fk->name};
567             }
568             }
569              
570 0 0         if($leave{'db'})
571             {
572 0 0         $self->{'db'}->dbh(undef) if($self->{'db'});
573             }
574             else
575             {
576 0           delete $self->{'db'};
577             }
578              
579             # Strip "on-save" code references: destructive!
580 0 0         unless($args{'strip_on_save_ok'})
581             {
582 0 0         if(__contains_code_ref($self->{ON_SAVE_ATTR_NAME()}))
583             {
584 0           croak qq(Refusing to strip "on-save" actions from ), ref($self),
585             qq( object without strip_on_save_ok parameter);
586             }
587             }
588              
589 0           delete $self->{ON_SAVE_ATTR_NAME()};
590              
591             # Reference to metadata object will be regenerated as needed
592 0           delete $self->{META_ATTR_NAME()};
593              
594 0           return $self;
595             }
596              
597             sub __contains_code_ref
598             {
599 0     0     my($hash_ref) = shift;
600              
601 0           foreach my $key (keys %$hash_ref)
602             {
603 0 0         return 1 if(ref $hash_ref->{$key} eq 'CODE');
604              
605 0 0         if(ref $hash_ref->{$key} eq 'HASH')
606             {
607 0 0         return 1 if(__contains_code_ref($hash_ref->{$key}));
608             }
609             else
610             {
611 0           Carp::confess "Unexpected reference encountered: $hash_ref->{$key}";
612             }
613             }
614             }
615              
616             # XXX: A value that is unlikely to exist in a primary key column value
617 63     63   680 use constant PK_JOIN => "\0\2,\3\0";
  63         155  
  63         9073  
618              
619             sub primary_key_as_string
620             {
621 0     0 0   my($self, $joiner) = @_;
622 0   0       return join($joiner || PK_JOIN, grep { defined } map { $self->$_() } $self->meta->primary_key_column_accessor_names);
  0            
  0            
623             }
624              
625 63     63   523 use constant DEFAULT_MAX_DEPTH => 100;
  63         153  
  63         17434  
626              
627             sub traverse_depth_first
628             {
629 0     0 1   my($self) = shift;
630              
631 0           my($context, $handlers, $exclude, $prune, $max_depth);
632              
633 0           my $visited = {};
634 0           my $force_load = 0;
635              
636 0 0         if(@_ == 1)
637             {
638 0           $handlers->{'object'} = shift;
639             }
640             else
641             {
642 0           my %args = @_;
643 0   0       $handlers = $args{'handlers'} || {};
644 0   0       $force_load = $args{'force_load'} || 0;
645 0           $context = $args{'context'};
646 0   0       $exclude = $args{'exclude'} || 0;
647 0           $prune = $args{'prune'};
648 0 0         $max_depth = exists $args{'max_depth'} ? $args{'max_depth'} : DEFAULT_MAX_DEPTH;
649 0 0         $visited = undef if($args{'allow_loops'});
650             }
651              
652 0   0       _traverse_depth_first($self, $context ||= {}, $handlers, $exclude, $prune, 0, $max_depth, undef, undef, $visited, $force_load);
653              
654 0           return $context;
655             }
656              
657             require Rose::DB::Object::Util;
658              
659 63     63   567 use constant OK => 1;
  63         159  
  63         4094  
660 63     63   450 use constant LOOP_AVOIDED => -1;
  63         194  
  63         3800  
661 63     63   468 use constant HIT_MAX_DEPTH => -2;
  63         163  
  63         3782  
662 63     63   570 use constant FILTERED_OUT => -3;
  63         195  
  63         126233  
663              
664             sub _traverse_depth_first
665             {
666 0     0     my($self, $context, $handlers, $exclude, $prune, $depth, $max_depth, $parent, $rel_meta, $visited, $force_load) = @_;
667              
668 0 0 0       if($visited && $visited->{ref($self),Rose::DB::Object::Helpers::primary_key_as_string($self)}++)
669             {
670 0           return LOOP_AVOIDED;
671             }
672              
673 0 0         if($handlers->{'object'})
674             {
675 0 0 0       if($exclude && $exclude->($self, $parent, $rel_meta))
676             {
677 0           return FILTERED_OUT;
678             }
679              
680 0 0 0       if($force_load && !Rose::DB::Object::Util::is_in_db($self))
681             {
682 0           $self->load(speculative => 1);
683             }
684              
685 0           $context = $handlers->{'object'}->($self, $context, $parent, $rel_meta, $depth);
686             }
687              
688 0 0 0       if(defined $max_depth && $depth == $max_depth)
689             {
690 0           return HIT_MAX_DEPTH;
691             }
692              
693 0           REL: foreach my $rel ($self->meta->relationships)
694             {
695 0 0 0       next if($prune && $prune->($rel, $self, $depth));
696              
697 0           my $objs = $rel->object_has_related_objects($self);
698             # XXX: Call above returns 0 if the collection is an empty array ref
699             # XXX: and undef if it's not even a reference (e.g., undef). This
700             # XXX: distinguishes between a collection that has been loaded and
701             # XXX: found to have zero items, and one that has never been loaded.
702             # XXX: To "un-hack" this, we'd need true tracking of load/store
703             # XXX: actions to related collections. Or we could just omit the
704             # XXX: empty collections from the traversal.
705 0 0 0       $objs = [] if(defined $objs && !ref $objs);
706              
707 0 0 0       if($force_load || $objs)
708             {
709 0 0         unless($objs)
710             {
711 0   0       my $method = $rel->method_name('get_set_on_save') ||
712             $rel->method_name('get_set_now') ||
713             $rel->method_name('get_set') ||
714             next REL;
715              
716 0   0       $objs = $self->$method() || next REL;
717 0 0         $objs = [ $objs ] unless(ref $objs eq 'ARRAY');
718             }
719              
720             my $c = $handlers->{'relationship'} ?
721 0 0         $handlers->{'relationship'}->($self, $context, $rel) : $context;
722              
723 0           OBJ: foreach my $obj (@$objs)
724             {
725 0 0 0       next OBJ if($exclude && $exclude->($obj, $self, $rel));
726              
727 0           my $ret = _traverse_depth_first($obj, $c, $handlers, $exclude, $prune, $depth + 1, $max_depth, $self, $rel, $visited, $force_load);
728              
729 0 0 0       if($ret == LOOP_AVOIDED && $handlers->{'loop_avoided'})
730             {
731 0 0         $handlers->{'loop_avoided'}->($obj, $c, $self, $context, $rel) && last OBJ;
732             }
733             }
734             }
735             }
736              
737 0           return OK;
738             }
739              
740             sub as_tree
741             {
742 0     0 1   my($self) = shift;
743              
744 0           my %args = @_;
745              
746 0 0         my $deflate = exists $args{'deflate'} ? $args{'deflate'} : 1;
747 0 0         my $persistent_columns_only = exists $args{'persistent_columns_only'} ? $args{'persistent_columns_only'} : 0;
748              
749 0           my %tree;
750              
751             Rose::DB::Object::Helpers::traverse_depth_first($self,
752             context => \%tree,
753             handlers =>
754             {
755             object => sub
756             {
757 0     0     my($self, $context, $parent, $relationship, $depth) = @_;
758              
759 0 0         local $self->{STATE_SAVING()} = 1 if($deflate);
760              
761 0           my $cols = Rose::DB::Object::Helpers::column_value_pairs($self);
762              
763 0 0         unless($persistent_columns_only)
764             {
765             # XXX: Inlined version of what would be nonpersistent_column_value_pairs()
766 0           my $methods = $self->meta->nonpersistent_column_accessor_method_names_hash;
767              
768 0           while(my($column, $method) = each(%$methods))
769             {
770 0           $cols->{$column} = $self->$method();
771             }
772             }
773              
774 0 0         if(ref $context eq 'ARRAY')
775             {
776 0           push(@$context, $cols);
777 0           return $cols;
778             }
779             else
780             {
781 0           @$context{keys %$cols} = values %$cols;
782 0           return $context;
783             }
784             },
785              
786             relationship => sub
787             {
788 0     0     my($self, $context, $relationship) = @_;
789              
790 0           my $name = $relationship->name;
791              
792             # Croak on name conflicts with columns
793 0 0         if($self->meta->column($name))
794             {
795 0           croak "$self: relationship '", $relationship->name,
796             "' conflicts with column of the same name";
797             }
798              
799 0 0         if($relationship->is_singular)
800             {
801 0           return $context->{$name} = {};
802             }
803              
804 0           return $context->{$name} = [];
805             },
806              
807             loop_avoided => sub
808             {
809 0     0     my($object, $context, $parent_object, $parent_context, $relationship) = @_;
810             # If any item can't be included due to loops, wipe entire collection and bail
811 0           delete $parent_context->{$relationship->name};
812 0           return 1; # true return means stop processing items in this collection
813             },
814             },
815 0           @_);
816              
817 0           return \%tree;
818             }
819              
820             # XXX: This version requires all relationship and column mutators to have
821             # XXX: the same names as the relationships and columns themselves.
822             # sub init_with_tree { shift->init(@_) }
823              
824             # XXX: This version requires all relationship mutators to have the same
825             # XXX: names as the relationships themselves.
826             # sub init_with_tree
827             # {
828             # my($self) = shift;
829             #
830             # my $meta = $self->meta;
831             #
832             # while(my($name, $value) = each(%{@_ == 1 ? $_[0] : {@_}}))
833             # {
834             # next unless(length $name);
835             # my $method;
836             #
837             # if(my $column = $meta->column($name))
838             # {
839             # $method = $column->mutator_method_name;
840             # $self->$method($value);
841             # }
842             # elsif($meta->relationship($name))
843             # {
844             # $self->$name($value);
845             # }
846             # }
847             #
848             # return $self;
849             # }
850              
851             our $Deflated = 0;
852              
853             sub init_with_deflated_tree
854             {
855 0     0 1   local $Deflated = 1;
856 0           Rose::DB::Object::Helpers::init_with_tree(@_);
857             }
858              
859             sub init_with_tree
860             {
861 0     0 1   my($self) = shift;
862              
863 0           my $meta = $self->meta;
864              
865 0           my %non_column;
866              
867             # Process all columns first
868 0 0         while(my($name, $value) = each(%{@_ == 1 ? $_[0] : {@_}}))
  0            
869             {
870 0 0         next unless(length $name);
871              
872 0 0         if(my $column = $meta->column($name))
873             {
874 0 0         local $self->{STATE_LOADING()} = 1 if($Deflated);
875 0           my $method = $column->mutator_method_name;
876 0           $self->$method($value);
877             }
878             else
879             {
880 0           $non_column{$name} = $value;
881             }
882             }
883              
884             # Process relationships and non-column attributes next
885 0           while(my($name, $value) = each(%non_column))
886             {
887 0 0         if(my $rel = $meta->relationship($name))
    0          
888             {
889 0   0       my $method = $rel->method_name('get_set_on_save') ||
890             $rel->method_name('get_set') ||
891             next;
892              
893 0           my $ref = ref $value;
894              
895 0 0         if($ref eq 'HASH')
    0          
896             {
897             # Split hash into relationship values and everything else
898 0           my %rel_vals;
899              
900 0 0         my %is_rel = map { $_->name => 1 } $rel->can('foreign_class') ?
  0            
901             $rel->foreign_class->meta->relationships : $rel->class->meta->relationships;
902              
903 0           foreach my $k (keys %$value)
904             {
905 0 0         $rel_vals{$k} = delete $value->{$k} if($is_rel{$k});
906             }
907              
908             # %$value now has non-relationship keys only
909 0           my $object = $self->$method(%$value);
910              
911             # Recurse on relationship key
912 0 0         Rose::DB::Object::Helpers::init_with_tree($object, \%rel_vals) if(%rel_vals);
913              
914             # Repair original hash
915 0           @$value{keys %rel_vals} = values %rel_vals;
916             }
917             elsif($ref eq 'ARRAY')
918             {
919 0           my(@objects, @sub_objects);
920              
921 0           foreach my $item (@$value)
922             {
923             # Split hash into relationship values and everything else
924 0           my %rel_vals;
925              
926 0 0         my %is_rel = map { $_->name => 1 } $rel->can('foreign_class') ?
  0            
927             $rel->foreign_class->meta->relationships : $rel->class->meta->relationships;
928              
929 0           foreach my $k (keys %$item)
930             {
931 0 0         $rel_vals{$k} = delete $item->{$k} if($is_rel{$k});
932             }
933              
934             # %$item now has non-relationship keys only
935 0           push(@objects, { %$item }); # shallow copy is sufficient
936              
937 0           push(@sub_objects, \%rel_vals);
938              
939             # Repair original hash
940 0           @$item{keys %rel_vals} = values %rel_vals;
941             }
942              
943             # Add the related objects
944 0           $self->$method(\@objects);
945              
946             # Recurse on the sub-objects
947 0           foreach my $object (@{ $self->$method() })
  0            
948             {
949 0           my $sub_objects = shift(@sub_objects);
950 0 0         Rose::DB::Object::Helpers::init_with_tree($object, $sub_objects) if(%$sub_objects);
951             }
952             }
953             else
954             {
955 0           Carp::cluck "Unknown reference encountered in $self tree: $name => $value";
956             }
957             }
958             elsif($self->can($name))
959             {
960 0           $self->$name($value);
961             }
962              
963             # XXX: Silently ignore all other name/value pairs
964             }
965              
966 0           return $self;
967             }
968              
969             sub new_from_tree
970             {
971 0     0 1   my $self = shift->new;
972 0           $self->Rose::DB::Object::Helpers::init_with_tree(@_);
973             }
974              
975             sub new_from_deflated_tree
976             {
977 0     0 1   my $self = shift->new;
978 0           $self->Rose::DB::Object::Helpers::init_with_deflated_tree(@_);
979             }
980              
981             __PACKAGE__->pre_import_hook(new_from_json => sub { require JSON });
982             __PACKAGE__->pre_import_hook(new_from_yaml => sub { require YAML::Syck });
983              
984 0     0 1   sub new_from_json { new_from_tree(shift, __PACKAGE__->json_decoder->decode(@_)) }
985 0     0 1   sub new_from_yaml { new_from_tree(shift, YAML::Syck::Load(@_)) }
986              
987             __PACKAGE__->pre_import_hook(init_with_json => sub { require JSON });
988             __PACKAGE__->pre_import_hook(init_with_yaml => sub { require YAML::Syck });
989              
990 0     0 1   sub init_with_json { init_with_tree(shift, __PACKAGE__->json_decoder->decode(@_)) }
991 0     0 1   sub init_with_yaml { init_with_tree(shift, YAML::Syck::Load(@_)) }
992              
993             __PACKAGE__->pre_import_hook(as_json => sub { require JSON });
994             __PACKAGE__->pre_import_hook(as_yaml => sub { require YAML::Syck });
995              
996 0     0 1   sub as_json { __PACKAGE__->json_encoder->encode(scalar as_tree(@_, deflate => 1)) }
997 0     0 1   sub as_yaml { YAML::Syck::Dump(scalar as_tree(@_, deflate => 1)) }
998              
999             sub dirty_columns
1000             {
1001 0     0 1   my($self) = shift;
1002              
1003 0 0         if(@_)
1004             {
1005 0           foreach my $column (@_)
1006             {
1007 0 0         my $name =
1008             UNIVERSAL::isa($column, 'Rose::DB::Object::Metadata::Column') ?
1009             $column->name : $column;
1010 0           Rose::DB::Object::Util::set_column_value_modified($self, $name);
1011             }
1012              
1013 0           return;
1014             }
1015              
1016 0           return wantarray ? keys %{$self->{MODIFIED_COLUMNS()}} :
1017 0 0         scalar keys %{$self->{MODIFIED_COLUMNS()}};
  0            
1018             }
1019              
1020             1;
1021              
1022             __END__
1023              
1024             =head1 NAME
1025              
1026             Rose::DB::Object::Helpers - A mix-in class containing convenience methods for Rose::DB::Object.
1027              
1028             =head1 SYNOPSIS
1029              
1030             package MyDBObject;
1031              
1032             use Rose::DB::Object;
1033             our @ISA = qw(Rose::DB::Object);
1034              
1035             use Rose::DB::Object::Helpers 'clone',
1036             { load_or_insert => 'find_or_create' };
1037             ...
1038              
1039             $obj = MyDBObject->new(id => 123);
1040             $obj->find_or_create();
1041              
1042             $obj2 = $obj->clone;
1043              
1044             =head1 DESCRIPTION
1045              
1046             L<Rose::DB::Object::Helpers> provides convenience methods from use with L<Rose::DB::Object>-derived classes. These methods do not exist in L<Rose::DB::Object> in order to keep the method namespace clean. (Each method added to L<Rose::DB::Object> is another potential naming conflict with a column accessor.)
1047              
1048             This class inherits from L<Rose::Object::MixIn>. See the L<Rose::Object::MixIn> documentation for a full explanation of how to import methods from this class. The helper methods themselves are described below.
1049              
1050             =head1 FUNCTIONS VS. METHODS
1051              
1052             Due to the "wonders" of Perl 5's object system, any helper method described here can also be used as a L<Rose::DB::Object::Util>-style utility I<function> that takes a L<Rose::DB::Object>-derived object as its first argument. Example:
1053              
1054             # Import two helpers
1055             use Rose::DB::Object::Helpers qw(clone_and_reset traverse_depth_first);
1056              
1057             $o = My::DB::Object->new(...);
1058              
1059             clone_and_reset($o); # Imported helper "method" called as function
1060              
1061             # Imported helper "method" with arguments called as function
1062             traverse_depth_first($o, handlers => { ... }, max_depth => 2);
1063              
1064             Why, then, the distinction between L<Rose::DB::Object::Helpers> methods and L<Rose::DB::Object::Util> functions? It's simply a matter of context. The functions in L<Rose::DB::Object::Util> are most useful in the context of the internals (e.g., writing your own L<column method-maker|Rose::DB::Object::Metadata::Column/"MAKING METHODS">) whereas L<Rose::DB::Object::Helpers> methods are most often added to a common L<Rose::DB::Object>-derived base class and then called as object methods by all classes that inherit from it.
1065              
1066             The point is, these are just conventions. Use any of these subroutines as functions or as methods as you see fit. Just don't forget to pass a L<Rose::DB::Object>-derived object as the first argument when calling as a function.
1067              
1068             =head1 OBJECT METHODS
1069              
1070             =head2 as_json [PARAMS]
1071              
1072             Returns a JSON-formatted string created from the object tree as created by the L<as_tree|/as_tree> method. PARAMS are the same as for the L<as_tree|/as_tree> method, except that the C<deflate> parameter is ignored (it is always set to true).
1073              
1074             You must have the L<JSON> module version 2.12 or later installed in order to use this helper method. If you have the L<JSON::XS> module version 2.2222 or later installed, this method will work a lot faster.
1075              
1076             =head2 as_tree [PARAMS]
1077              
1078             Returns a reference to a hash of name/value pairs representing the column values of this object as well as any nested sub-objects. The PARAMS name/value pairs dictate the details of the sub-object traversal. Valid parameters are:
1079              
1080             =over 4
1081              
1082             =item B<allow_loops BOOL>
1083              
1084             If true, allow loops during the traversal (e.g., A -E<gt> B -E<gt> C -E<gt> A). The default value is false.
1085              
1086             =item B<deflate BOOL>
1087              
1088             If true, the values in the tree will be simple scalars suitable for storage in the database (e.g., a date string like "2005-12-31" instead of a L<DateTime> object). The default is true.
1089              
1090             =item B<exclude CODEREF>
1091              
1092             A reference to a subroutine that is called on each L<Rose::DB::Object>-derived object encountered during the traversal. It is passed the object, the parent object (undef, if none), and the L<Rose::DB::Object::Metadata::Relationship>-derived object (undef, if none) that led to this object. If the subroutine returns true, then this object is not processed. Example:
1093              
1094             exclude => sub
1095             {
1096             my($object, $parent, $rel_meta) = @_;
1097             ...
1098             return 1 if($should_exclude);
1099             return 0;
1100             },
1101              
1102             =item B<force_load BOOL>
1103              
1104             If true, related sub-objects will be loaded from the database. If false, then only the sub-objects that have already been loaded from the database will be traversed. The default is false.
1105              
1106             =item B<max_depth DEPTH>
1107              
1108             Do not descend past DEPTH levels. Depth is an integer starting from 0 for the object that the L<as_tree|/as_tree> method was called on and increasing with each level of related objects. The default value is 100.
1109              
1110             =item B<persistent_columns_only BOOL>
1111              
1112             If true, L<non-persistent columns|Rose::DB::Object::Metadata/nonpersistent_columns> will not be included in the tree. The default is false.
1113              
1114             =item B<prune CODEREF>
1115              
1116             A reference to a subroutine that is called on each L<Rose::DB::Object::Metadata::Relationship>-derived object encountered during traversal. It is passed the relationship object, the parent object, and the depth. If the subroutine returns true, then the entire sub-tree below this relationship will not be traversed. Example:
1117              
1118             prune => sub
1119             {
1120             my($rel_meta, $object, $depth) = @_;
1121             ...
1122             return 1 if($should_prune);
1123             return 0;
1124             },
1125              
1126             =back
1127              
1128             B<Caveats>: Currently, you cannot have a relationship and a column with the same name in the same class. This should not happen without explicit action on the part of the class creator, but it is technically possible. The result of serializing such an object using L<as_tree|/as_tree> is undefined. This limitation may be removed in the future.
1129              
1130             The exact format of the "tree" data structure returned by this method is not public and may change in the future (e.g., to overcome the limitation described above).
1131              
1132             =head2 as_yaml [PARAMS]
1133              
1134             Returns a YAML-formatted string created from the object tree as created by the L<as_tree|/as_tree> method. PARAMS are the same as for the L<as_tree|/as_tree> method, except that the C<deflate> parameter is ignored (it is always set to true).
1135              
1136             You must have the L<YAML::Syck> module installed in order to use this helper method.
1137              
1138             =head2 clone
1139              
1140             Returns a new object initialized with the column values of the existing object. For example, imagine a C<Person> class with three columns, C<id>, C<name>, and C<age>.
1141              
1142             $a = Person->new(id => 123, name => 'John', age => 30);
1143              
1144             This use of the C<clone()> method:
1145              
1146             $b = $a->clone;
1147              
1148             is equivalent to this:
1149              
1150             $b = Person->new(id => $a->id, name => $a->name, age => $a->age);
1151              
1152             =head2 clone_and_reset
1153              
1154             This is the same as the L<clone|/clone> method described above, except that it also sets all of the L<primary|Rose::DB::Object::Metadata/primary_key_columns> and L<unique key columns|Rose::DB::Object::Metadata/unique_keys> to undef. If the cloned object has a L<db|Rose::DB::Object/db> attribute, then it is copied to the clone object as well.
1155              
1156             For example, imagine a C<Person> class with three columns, C<id>, C<name>, and C<age>, where C<id> is the primary key and C<name> is a unique key.
1157              
1158             $a = Person->new(id => 123, name => 'John', age => 30, db => $db);
1159              
1160             This use of the C<clone_and_reset()> method:
1161              
1162             $b = $a->clone_and_reset;
1163              
1164             is equivalent to this:
1165              
1166             $b = Person->new(id => $a->id, name => $a->name, age => $a->age);
1167             $b->id(undef); # reset primary key
1168             $b->name(undef); # reset unique key
1169             $b->db($a->db); # copy db
1170              
1171             =head2 column_values_as_json
1172              
1173             Returns a string containing a JSON representation of the object's column values. You must have the L<JSON> module version 2.12 or later installed in order to use this helper method. If you have the L<JSON::XS> module version 2.2222 or later installed, this method will work a lot faster.
1174              
1175             =head2 column_values_as_yaml
1176              
1177             Returns a string containing a YAML representation of the object's column values. You must have the L<YAML::Syck> module installed in order to use this helper method.
1178              
1179             =head2 column_accessor_value_pairs
1180              
1181             Returns a hash (in list context) or reference to a hash (in scalar context) of column accessor method names and column values. The keys of the hash are the L<accessor method names|Rose::DB::Object::Metadata::Column/accessor_method_name> for the columns. The values are retrieved by calling the L<accessor method|Rose::DB::Object::Metadata::Column/accessor_method_name> for each column.
1182              
1183             =head2 column_mutator_value_pairs
1184              
1185             Returns a hash (in list context) or reference to a hash (in scalar context) of column mutator method names and column values. The keys of the hash are the L<mutator method names|Rose::DB::Object::Metadata::Column/mutator_method_name> for the columns. The values are retrieved by calling the L<accessor method|Rose::DB::Object::Metadata::Column/accessor_method_name> for each column.
1186              
1187             =head2 column_value_pairs
1188              
1189             Returns a hash (in list context) or reference to a hash (in scalar context) of column name and value pairs. The keys of the hash are the L<names|Rose::DB::Object::Metadata::Column/name> of the columns. The values are retrieved by calling the L<accessor method|Rose::DB::Object::Metadata::Column/accessor_method_name> for each column.
1190              
1191             =head2 dirty_columns [ NAMES | COLUMNS ]
1192              
1193             Given a list of column names or L<Rose::DB::Object::Metadata::Column>-derived objects, mark each column in the invoking object as L<modifed|Rose::DB::Object::Util/set_column_value_modified>.
1194              
1195             If passed no arguments, returns a list of all modified columns in list context or the number of modified columns in scalar context.
1196              
1197             =head2 forget_related [ NAME | PARAMS ]
1198              
1199             Given a foreign key or relationship name, forget any L<previously loaded|/has_loaded_related> objects related by the specified foreign key or relationship. Normally, any objects loaded by the default accessor methods for relationships and foreign keys are fetched from the database only the first time they are asked for, and simply returned thereafter. Asking them to be "forgotten" causes them to be fetched anew from the database the next time they are asked for.
1200              
1201             If the related object name is passed as a plain string NAME, then a foreign key with that name is looked up. If no such foreign key exists, then a relationship with that name is looked up. If no such relationship or foreign key exists, a fatal error will occur. Example:
1202              
1203             $foo->forget_related('bar');
1204              
1205             It's generally not a good idea to add a foreign key and a relationship with the same name, but it is technically possible. To specify the domain of the name, pass the name as the value of a C<foreign_key> or C<relationship> parameter. Example:
1206              
1207             $foo->forget_related(foreign_key => 'bar');
1208             $foo->forget_related(relationship => 'bar');
1209              
1210             =head2 has_loaded_related [ NAME | PARAMS ]
1211              
1212             Given a foreign key or relationship name, return true if one or more related objects have been loaded into the current object, false otherwise.
1213              
1214             If the name is passed as a plain string NAME, then a foreign key with that name is looked up. If no such foreign key exists, then a relationship with that name is looked up. If no such relationship or foreign key exists, a fatal error will occur. Example:
1215              
1216             $foo->has_loaded_related('bar');
1217              
1218             It's generally not a good idea to add a foreign key and a relationship with the same name, but it is technically possible. To specify the domain of the name, pass the name as the value of a C<foreign_key> or C<relationship> parameter. Example:
1219              
1220             $foo->has_loaded_related(foreign_key => 'bar');
1221             $foo->has_loaded_related(relationship => 'bar');
1222              
1223             =head2 init_with_column_value_pairs [ HASH | HASHREF ]
1224              
1225             Initialize an object with a hash or reference to a hash of column/value pairs. This differs from the inherited L<init|Rose::Object/init> method in that it accepts column names rather than method names. A column name may not be the same as its mutator method name if the column is L<aliased|Rose::DB::Object::Metadata/alias_column>, for example.
1226              
1227             $p = Person->new; # assume "type" column is aliased to "person_type"
1228              
1229             # init() takes method/value pairs
1230             $p->init(person_type => 'cool', age => 30);
1231              
1232             # Helper takes a hashref of column/value pairs
1233             $p->init_with_column_value_pairs({ type => 'cool', age => 30 });
1234              
1235             # ...or a hash of column/value pairs
1236             $p->init_with_column_value_pairs(type => 'cool', age => 30);
1237              
1238             =head2 init_with_json JSON
1239              
1240             Initialize the object with a JSON-formatted string. The JSON string must be in the format returned by the L<as_json|/as_json> (or L<column_values_as_json|/column_values_as_json>) method. Example:
1241              
1242             $p1 = Person->new(name => 'John', age => 30);
1243             $json = $p1->as_json;
1244              
1245             $p2 = Person->new;
1246             $p2->init_with_json($json);
1247              
1248             print $p2->name; # John
1249             print $p2->age; # 30
1250              
1251             =head2 init_with_deflated_tree TREE
1252              
1253             This is the same as the L<init_with_tree|/init_with_tree> method, except that it expects all the values to be simple scalars suitable for storage in the database (e.g., a date string like "2005-12-31" instead of a L<DateTime> object). In other words, the TREE should be in the format generated by the L<as_tree|/as_tree> method called with the C<deflate> parameter set to true. Initializing objects in this way is slightly more efficient.
1254              
1255             =head2 init_with_tree TREE
1256              
1257             Initialize the object with a Perl data structure in the format returned from the L<as_tree|/as_tree> method. Example:
1258              
1259             $p1 = Person->new(name => 'John', age => 30);
1260             $tree = $p1->as_tree;
1261              
1262             $p2 = Person->new;
1263             $p2->init_with_tree($tree);
1264              
1265             print $p2->name; # John
1266             print $p2->age; # 30
1267              
1268             =head2 init_with_yaml YAML
1269              
1270             Initialize the object with a YAML-formatted string. The YAML string must be in the format returned by the L<as_yaml|/as_yaml> (or L<column_values_as_yaml|/column_values_as_yaml>) method. Example:
1271              
1272             $p1 = Person->new(name => 'John', age => 30);
1273             $yaml = $p1->as_yaml;
1274              
1275             $p2 = Person->new;
1276             $p2->init_with_yaml($yaml);
1277              
1278             print $p2->name; # John
1279             print $p2->age; # 30
1280              
1281             =head2 insert_or_update [PARAMS]
1282              
1283             If the object already exists in the database, then update it. Otherwise, insert it. Any PARAMS are passed on to the call to L<save|Rose::DB::Object/save> (which is supplied with the appropriate C<insert> or C<update> boolean parameter).
1284              
1285             This method differs from the standard L<save|Rose::DB::Object/save> method in that L<save|Rose::DB::Object/save> decides to L<insert|Rose::DB::Object/insert> or L<update|Rose::DB::Object/update> based solely on whether or not the object was previously L<load|Rose::DB::Object/load>ed. This method will take the extra step of actually attempting to L<load|Rose::DB::Object/load> the object to see whether or not it's in the database.
1286              
1287             The return value of the L<save|Rose::DB::Object/save> method is returned.
1288              
1289             =head2 insert_or_update_on_duplicate_key [PARAMS]
1290              
1291             Update or insert a row with a single SQL statement, depending on whether or not a row with the same primary or unique key already exists. Any PARAMS are passed on to the call to L<save|Rose::DB::Object/save> (which is supplied with the appropriate C<insert> or C<update> boolean parameter).
1292              
1293             If the current database does not support the "ON DUPLICATE KEY UPDATE" SQL extension, then this method simply calls the L<insert_or_update|/insert_or_update> method, passing all PARAMS.
1294              
1295             Currently, the only database that supports "ON DUPLICATE KEY UPDATE" is MySQL, and only in version 4.1.0 or later. You can read more about the feature here:
1296              
1297             L<http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html>
1298              
1299             Here's a quick example of the SQL syntax:
1300              
1301             INSERT INTO table (a, b, c) VALUES (1, 2, 3)
1302             ON DUPLICATE KEY UPDATE a = 1, b = 2, c = 3;
1303              
1304             Note that there are two sets of columns and values in the statement. This presents a choice: which columns to put in the "INSERT" part, and which to put in the "UPDATE" part.
1305              
1306             When using this method, if the object was previously L<load|Rose::DB::Object/load>ed from the database, then values for all columns are put in both the "INSERT" and "UPDATE" portions of the statement.
1307              
1308             Otherwise, all columns are included in both clauses I<except> those belonging to primary keys or unique keys which have only undefined values. This is important because it allows objects to be updated based on a single primary or unique key, even if other possible keys exist, but do not have values set. For example, consider this table with the following data:
1309              
1310             CREATE TABLE parts
1311             (
1312             id INT PRIMARY KEY,
1313             code CHAR(3) NOT NULL,
1314             status CHAR(1),
1315              
1316             UNIQUE(code)
1317             );
1318              
1319             INSERT INTO parts (id, code, status) VALUES (1, 'abc', 'x');
1320              
1321             This code will update part id 1, setting its "status" column to "y".
1322              
1323             $p = Part->new(code => 'abc', status => 'y');
1324             $p->insert_or_update_on_duplicate_key;
1325              
1326             The resulting SQL:
1327              
1328             INSERT INTO parts (code, status) VALUES ('abc', 'y')
1329             ON DUPLICATE KEY UPDATE code = 'abc', status = 'y';
1330              
1331             Note that the "id" column is omitted because it has an undefined value. The SQL statement will detect the duplicate value for the unique key "code" and then run the "UPDATE" portion of the query, setting "status" to "y".
1332              
1333             This method returns true if the row was inserted or updated successfully, false otherwise. The true value returned on success will be the object itself. If the object L<overload>s its boolean value such that it is not true, then a true value will be returned instead of the object itself.
1334              
1335             Yes, this method name is very long. Remember that you can rename methods on import. It is expected that most people will want to rename this method to "insert_or_update", using it in place of the normal L<insert_or_update|/insert_or_update> helper method:
1336              
1337             package My::DB::Object;
1338             ...
1339             use Rose::DB::Object::Helpers
1340             { insert_or_update_on_duplicate_key => 'insert_or_update' };
1341              
1342             =head2 load_or_insert [PARAMS]
1343              
1344             Try to L<load|Rose::DB::Object/load> the object, passing PARAMS to the call to the L<load()|Rose::DB::Object/load> method. The parameter "speculative => 1" is automatically added to PARAMS. If no such object is found, then the object is L<insert|Rose::DB::Object/insert>ed.
1345              
1346             Example:
1347              
1348             # Get object id 123 if it exists, otherwise create it now.
1349             $obj = MyDBObject->new(id => 123)->load_or_insert;
1350              
1351             =head2 load_or_save [PARAMS]
1352              
1353             Try to L<load|Rose::DB::Object/load> the object, passing PARAMS to the call to the L<load()|Rose::DB::Object/load> method. The parameter "speculative => 1" is automatically added to PARAMS. If no such object is found, then the object is L<save|Rose::DB::Object/save>d.
1354              
1355             This methods differs from L<load_or_insert|/load_or_insert> in that the L<save|Rose::DB::Object/save> method will also save sub-objects. See the documentation for L<Rose::DB::Object>'s L<save|Rose::DB::Object/save> method for more information.
1356              
1357             Example:
1358              
1359             @perms = (Permission->new(...), Permission->new(...));
1360              
1361             # Get person id 123 if it exists, otherwise create it now
1362             # along with permission sub-objects.
1363             $person = Person->new(id => 123, perms => \@perms)->load_or_save;
1364              
1365             =head2 load_speculative [PARAMS]
1366              
1367             Try to L<load|Rose::DB::Object/load> the object, passing PARAMS to the call to the L<load()|Rose::DB::Object/load> method along with the "speculative => 1" parameter. See the documentation for L<Rose::DB::Object>'s L<load|Rose::DB::Object/load> method for more information.
1368              
1369             Example:
1370              
1371             $obj = MyDBObject->new(id => 123);
1372              
1373             if($obj->load_speculative)
1374             {
1375             print "Found object id 123\n";
1376             }
1377             else
1378             {
1379             print "Object id 123 not found\n";
1380             }
1381              
1382             =head2 new_from_json JSON
1383              
1384             The method is the equivalent of creating a new object and then calling the L<init_with_json|/init_with_json> method on it, passing JSON as an argument. See the L<init_with_json|/init_with_json> method for more information.
1385              
1386             =head2 new_from_deflated_tree TREE
1387              
1388             The method is the equivalent of creating a new object and then calling the L<init_with_deflated_tree|/init_with_deflated_tree> method on it, passing TREE as an argument. See the L<init_with_deflated_tree|/init_with_deflated_tree> method for more information.
1389              
1390             =head2 new_from_tree TREE
1391              
1392             The method is the equivalent of creating a new object and then calling the L<init_with_tree|/init_with_tree> method on it, passing TREE as an argument. See the L<init_with_tree|/init_with_tree> method for more information.
1393              
1394             =head2 new_from_yaml YAML
1395              
1396             The method is the equivalent of creating a new object and then calling the L<init_with_yaml|/init_with_yaml> method on it, passing YAML as an argument. See the L<init_with_yaml|/init_with_yaml> method for more information.
1397              
1398             =head2 strip [PARAMS]
1399              
1400             This method prepares an object for serialization by stripping out internal structures known to contain code references or other values that do not survive serialization. The object itself is returned, now stripped.
1401              
1402             B<Note:> Operations that were scheduled to happen "on L<save()|Rose::DB::Object/save>" will I<also> be stripped out by this method. Examples include the databsae update or insertion of any child objects attached to the parent object using C<get_set_on_save>, C<add_on_save>, or C<delete_on_save> methods. If such operations exist, an exception will be thrown unless the C<strip_on_save_ok> parameter is true.
1403              
1404             If your object has these kinds of pending changes, either L<save()|Rose::DB::Object/save> first and then L<strip()|/strip>, or L<clone()|/clone> and then L<strip()|/strip> the clone.
1405              
1406             By default, the L<db|Rose::DB::Object/db> object and all sub-objects (foreign keys or relationships) are removed. PARAMS are optional name/value pairs. Valid PARAMS are:
1407              
1408             =over 4
1409              
1410             =item B<leave [ NAME | ARRAYREF ]>
1411              
1412             This parameter specifies which items to leave un-stripped. The value may be an item name or a reference to an array of item names. Valid names are:
1413              
1414             =over 4
1415              
1416             =item B<db>
1417              
1418             Do not remove the L<db|Rose::DB::Object/db> object. The L<db|Rose::DB::Object/db> object will have its DBI database handle (L<dbh|Rose::DB/dbh>) removed, however.
1419              
1420             =item B<foreign_keys>
1421              
1422             Do not removed sub-objects that have L<already been loaded|/has_loaded_related> by this object through L<foreign keys|Rose::DB::Object::Metadata/foreign_keys>.
1423              
1424             =item B<relationships>
1425              
1426             Do not removed sub-objects that have L<already been loaded|/has_loaded_related> by this object through L<relationships|Rose::DB::Object::Metadata/relationships>.
1427              
1428             =item B<related_objects>
1429              
1430             Do not remove any sub-objects (L<foreign keys|Rose::DB::Object::Metadata/foreign_keys> or L<relationships|Rose::DB::Object::Metadata/relationships>) that have L<already been loaded|/has_loaded_related> by this object. This option is the same as specifying both the C<foreign_keys> and C<relationships> names.
1431              
1432             =back
1433              
1434             =item B<strip_on_save_ok BOOL>
1435              
1436             If true, do not throw an exception when pending "on-save" changes exist in the object; just strip them. (See description above for details.)
1437              
1438             =back
1439              
1440             =head2 B<traverse_depth_first [ CODEREF | PARAMS ]>
1441              
1442             Do a depth-first traversal of the L<Rose::DB::Object>-derived object that this method is called on, descending into related objects. If a reference to a subroutine is passed as the sole argument, it is taken as the value of the C<object> key to the C<handlers> parameter hash (see below). Otherwise, PARAMS name/value pairs are expected. Valid parameters are:
1443              
1444             =over 4
1445              
1446             =item B<allow_loops BOOL>
1447              
1448             If true, allow loops during the traversal (e.g., A -E<gt> B -E<gt> C -E<gt> A). The default value is false.
1449              
1450             =item B<context SCALAR>
1451              
1452             An arbitrary context variable to be passed along to (and possibly modified by) each handler routine (see C<handlers> parameter below). The context may be any scalar value (e.g., an object, a reference to a hash, etc.)
1453              
1454             =item B<exclude CODEREF>
1455              
1456             A reference to a subroutine that is called on each L<Rose::DB::Object>-derived object encountered during the traversal. It is passed the object, the parent object (undef, if none), and the L<Rose::DB::Object::Metadata::Relationship>-derived object (undef, if none) that led to this object. If the subroutine returns true, then this object is not processed. Example:
1457              
1458             exclude => sub
1459             {
1460             my($object, $parent, $rel_meta) = @_;
1461             ...
1462             return 1 if($should_exclude);
1463             return 0;
1464             },
1465              
1466             =item B<force_load BOOL>
1467              
1468             If true, related sub-objects will be loaded from the database. If false, then only the sub-objects that have already been loaded from the database will be traversed. The default is false.
1469              
1470             =item B<handlers HASHREF>
1471              
1472             A reference to a hash of handler subroutines. Valid keys, calling context, and the arguments passed to the referenced subroutines are as follows.
1473              
1474             =over 4
1475              
1476             =item B<object>
1477              
1478             This handler is called whenever a L<Rose::DB::Object>-derived object is encountered. This includes the object that L<traverse_depth_first|/traverse_depth_first> was called on as well as any sub-objects. The handler is passed the object, the C<context>, the parent object (undef, if none), the L<Rose::DB::Object::Metadata::Relationship>-derived object through which this object was arrived at (undef if none), and the depth.
1479              
1480             The handler I<must> return the value to be used as the C<context> during the traversal of any related sub-objects. The context returned may be different than the context passed in. Example:
1481              
1482             handlers =>
1483             {
1484             object => sub
1485             {
1486             my($object, $context, $parent, $rel_meta, $depth) = @_;
1487             ...
1488              
1489             return $context; # Important!
1490             }
1491             ...
1492             }
1493              
1494             =item B<relationship>
1495              
1496             This handler is called just before a L<Rose::DB::Object::Metadata::Relationship>-derived object is descended into (i.e., just before the sub-objectes related through this relationship are processed). The handler is passed the object that contains the relationship, the C<context>, the C<context>, and the L<relationship|Rose::DB::Object::Metadata::Relationship> object itself.
1497              
1498             The handler I<must> return the value to be used as the C<context> during the traversal of the objects related through this relationship. (If you do not define this handler, then the current context object will be used.) The context returned may be different than the context passed in. Example:
1499              
1500             handlers =>
1501             {
1502             relationship => sub
1503             {
1504             my($object, $context, $rel_meta) = @_;
1505             ...
1506              
1507             return $context; # Important!
1508             }
1509             ...
1510             }
1511              
1512             =item B<loop_avoided>
1513              
1514             This handler is called after the traversal refuses to process a sub-object in order to avoid a loop. (This only happens if the C<allow_loops> is parameter is false, obviously.) The handler is passed the object that was not processed, the C<context>, the parent object, the I<previous> C<context>, and the L<Rose::DB::Object::Metadata::Relationship>-derived object through which the sub-object was related. Example:
1515              
1516             handlers =>
1517             {
1518             loop_avoided => sub
1519             {
1520             my($object, $context, $parent, $prev_context, $rel_meta) = @_;
1521             ...
1522             }
1523             ...
1524             }
1525              
1526             =back
1527              
1528             =item B<max_depth DEPTH>
1529              
1530             Do not descend past DEPTH levels. Depth is an integer starting from 0 for the object that the L<traverse_depth_first|/traverse_depth_first> method was called on and increasing with each level of related objects. The default value is 100.
1531              
1532             =item B<prune CODEREF>
1533              
1534             A reference to a subroutine that is called on each L<Rose::DB::Object::Metadata::Relationship>-derived object encountered during traversal. It is passed the relationship object, the parent object, and the depth. If the subroutine returns true, then the entire sub-tree below this relationship will not be traversed. Example:
1535              
1536             prune => sub
1537             {
1538             my($rel_meta, $object, $depth) = @_;
1539             ...
1540             return 1 if($should_prune);
1541             return 0;
1542             },
1543              
1544             =back
1545              
1546             =head1 AUTHOR
1547              
1548             John C. Siracusa (siracusa@gmail.com)
1549              
1550             =head1 LICENSE
1551              
1552             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is
1553             free software; you can redistribute it and/or modify it under the same terms
1554             as Perl itself.