File Coverage

lib/UR/Object.pm
Criterion Covered Total %
statement 248 352 70.4
branch 89 174 51.1
condition 47 93 50.5
subroutine 41 47 87.2
pod 13 17 76.4
total 438 683 64.1


line stmt bran cond sub pod time code
1             package UR::Object;
2              
3 266     266   918 use warnings;
  266         422  
  266         7683  
4 266     266   1024 use strict;
  266         286  
  266         6442  
5              
6             require UR;
7              
8 266     266   846 use Scalar::Util qw(looks_like_number refaddr isweak);
  266         300  
  266         21145  
9 266     266   119916 use List::MoreUtils qw(any);
  266         1974847  
  266         1351  
10              
11             our @ISA = ('UR::ModuleBase');
12             our $VERSION = "0.46"; # UR $VERSION;;
13              
14             # Base object API
15              
16 35773 100   35773 1 134480 sub class { ref($_[0]) || $_[0] }
17              
18 10887457     10887457 1 19547617 sub id { $_[0]->{id} }
19              
20             sub create {
21 3580     3580 1 86697 $UR::Context::current->create_entity(@_);
22             }
23              
24             sub get {
25 246656     246656 1 967009 $UR::Context::current->query(@_);
26             }
27              
28             sub delete {
29 672     672 1 11094 $UR::Context::current->delete_entity(@_);
30             }
31              
32             sub copy {
33 3     3 1 14 my $self = shift;
34 3         9 my %override = @_;
35              
36 3         13 my $meta = $self->__meta__;
37             my @copyable_properties =
38 3   100     92 grep { !$_->is_delegated && !$_->is_id }
  8         18  
39             $meta->properties;
40              
41 3         7 my %params;
42 3         6 for my $p (@copyable_properties) {
43 3         11 my $name = $p->property_name;
44 3 100       10 if ($p->is_many) {
45 1 50       4 if (my @value = $self->$name) {
46 1         4 $params{$name} = \@value;
47             }
48             }
49             else {
50 2 50       8 if (defined(my $value = $self->$name)) {
51 2         5 $params{$name} = $value;
52             }
53             }
54             }
55              
56 3         14 return $self->class->create(%params, %override);
57             }
58              
59              
60             # Meta API
61              
62             sub __context__ {
63             # In UR, a "context" handles inter-object references so they can cross
64             # process boundaries, and interact with persistence systems automatically.
65              
66             # For efficiency, all context switches update a package-level value.
67              
68             # We will ultimately need to support objects recording their context explicitly
69             # for things such as data maintenance operations. This shouldn't happen
70             # during "business logic".
71              
72 0     0   0 return $UR::Context::current;
73             }
74              
75             sub __meta__ {
76             # the class meta object
77             # subclasses set this specifically for efficiency upon construction
78             # the base class has a generic implementation for boostrapping
79             Carp::cluck("using the default __meta__!");
80             my $class_name = shift;
81             return $UR::Context::all_objects_loaded->{"UR::Object::Type"}{$class_name};
82             }
83              
84             # The identity operation. Not particularly useful by itself, but makes
85             # things like mapping operations easier and calculate_from metadata able
86             # to include the object as function args to calculated properties
87             sub __self__ {
88 24 50   24   94 return $_[0] if @_ == 1;
89 0         0 my $self = shift;
90 0         0 my $bx = $self->class->define_boolexpr(@_);
91 0 0       0 if ($bx->evaluate($self)) {
92 0         0 return $self;
93             }
94             else {
95 0         0 return;
96             }
97             }
98              
99             sub does {
100 7     7 0 1852 my($self, $role_name) = @_;
101              
102 7         20 my @roles = map { @{ $_->roles } }
  18         16  
  18         46  
103             $self->__meta__->all_class_metas();
104              
105 7     9   40 any { $role_name eq $_->role_name } @roles;
  9         18  
106             }
107              
108              
109             # Used to traverse n levels of indirect properties, even if the total
110             # indirection is not defined on the primary ofhect this is called on.
111             # For example: $obj->__get_attr__('a.b.c');
112             # gets $obj's 'a' value, calls 'b' on that, and calls 'c' on the last thing
113             sub __get_attr__ {
114 2126     2126   2193 my ($self, $property_name) = @_;
115 2126         1667 my @property_values;
116 2126 100       3972 if (index($property_name,'.') == -1) {
117 2096         5216 @property_values = $self->$property_name;
118             }
119             else {
120 30         92 my @links = split(/\./,$property_name);
121 30         57 @property_values = ($self);
122 30         90 for my $full_link (@links) {
123 60         102 my $pos = index($full_link,'-');
124 60 50       121 my $link = ($pos == -1 ? $full_link : substr($full_link,0,$pos) );
125 60 50       86 @property_values = map { defined($_) ? $_->$link : undef } @property_values;
  63         258  
126             }
127             }
128 2126 50       3664 return if not defined wantarray;
129 2126 50       5982 return @property_values if wantarray;
130 0 0       0 if (@property_values > 1) {
131 0         0 my $class_name = $self->__meta__->class_name;
132 0         0 Carp::confess("Multiple values returned for $class_name $property_name in scalar context!");
133             }
134 0         0 return $property_values[0];
135             }
136              
137             sub __label_name__ {
138             # override to provide default labeling of the object
139 0     0   0 my $self = $_[0];
140 0   0     0 my $class = ref($self) || $self;
141 0         0 my ($label) = ($class =~ /([^:]+)$/);
142 0         0 $label =~ s/([a-z])([A-Z])/$1 $2/g;
143 0         0 $label =~ s/([A-Z])([A-Z]([a-z]|\s|$))/$1 $2/g;
144 0 0       0 $label = uc($label) if $label =~ /_id$/i;
145 0         0 return $label;
146             }
147              
148             sub __display_name__ {
149             # default stringification (does override "" unless you specifically choose to)
150 5     5   7 my $self = shift;
151 5         9 my $in_context_of_related_object = shift;
152              
153 5         9 my $name = $self->id;
154 5         11 $name =~ s/\t/ /g;
155 5         249 return $name;
156              
157 0 0       0 if (not $in_context_of_related_object) {
    0          
158             # no in_context_of_related_object.
159             # the object is identified globally
160 0         0 return $self->label_name . ' ' . $name;
161             }
162             elsif ($in_context_of_related_object eq ref($self)) {
163             # the class is completely known
164             # show only the core display name
165             # -> less text, more in_context_of_related_object
166 0         0 return $name
167             }
168             else {
169             # some intermediate base class is known,
170             # TODO: make this smarter
171             # For now, just show the whole class name with the ID
172 0         0 return $self->label_name . ' ' . $name;
173             }
174             }
175              
176             sub __errors__ {
177             # This is the basis for software constraint checking.
178             # Return a list of values describing the problems on the object.
179              
180 2995     2995   5158 my ($self,@property_names) = @_;
181              
182 2995         7455 my $class_object = $self->__meta__;
183              
184 2995 50       6058 unless (scalar @property_names) {
185 2995         11474 @property_names = $class_object->all_property_names;
186             }
187              
188             my @properties = map {
189 2995         5230 $class_object->property_meta_for_name($_);
  18850         27086  
190             } @property_names;
191              
192 2995         3790 my @tags;
193 2995         4433 for my $property_metadata (@properties) {
194             # For now we don't validate these.
195             # Ultimately, we should delegate to the property metadata object for value validation.
196             my($is_delegated, $is_calculated, $property_name, $is_optional, $generic_data_type, $data_length)
197 18850         35434 = @$property_metadata{'is_delegated','is_calculated','property_name','is_optional', 'data_type','data_length'};
198              
199 18850 100 100     51132 next if $is_delegated || $is_calculated;
200              
201             # TODO: is this making commits slow by calling lots of indirect accessors?
202 17946         43416 my @values = $self->$property_name;
203 17946 100       25739 next if @values > 1;
204              
205 17944         14644 my $value = $values[0];
206              
207             # account for minus sign in dummy ID
208 17944 0 33     29550 if ($ENV{UR_USE_DUMMY_AUTOGENERATED_IDS} and $property_metadata->is_id and defined($value) and index($value, '-') == 0 and defined $data_length) {
      33        
      0        
      0        
209 0         0 $data_length++;
210             }
211              
212 17944 100 100     37359 if (! ($is_optional or defined($value))) {
213 95         630 push @tags, UR::Object::Tag->create(
214             type => 'invalid',
215             properties => [$property_name],
216             desc => "No value specified for required property",
217             );
218             }
219              
220             # The tests below don't apply do undefined values.
221             # Save the trouble and move on.
222 17944 100       23800 next unless defined $value;
223              
224             # Check data type
225             # TODO: delegate to the data type module for this
226 13643 100       16780 $generic_data_type = '' unless (defined $generic_data_type);
227              
228 13643 100 100     43640 if ($generic_data_type eq 'Float' || $generic_data_type eq 'Integer') {
    50          
229 1918 100       6437 if (looks_like_number($value)) {
230 1914         2401 $value = $value + 0;
231             }
232             else{
233 4         20 push @tags, UR::Object::Tag->create (
234             type => 'invalid',
235             properties => [$property_name],
236             desc => "Invalid $generic_data_type value."
237             );
238             }
239             }
240             elsif ($generic_data_type eq 'DateTime') {
241             # This check is currently disabled b/c of time format irrecularities
242             # We rely on underlying database constraints for real invalidity checking.
243             # TODO: fix me
244 0         0 if (1) {
245              
246             }
247             elsif ($value =~ /^\s*\d\d\d\d\-\d\d-\d\d\s*(\d\d:\d\d:\d\d|)\s*$/) {
248             # TODO more validation here for a real date.
249             }
250             else {
251             push @tags, UR::Object::Tag->create (
252             type => 'invalid',
253             properties => [$property_name],
254             desc => 'Invalid date string.'
255             );
256             }
257             }
258              
259             # Check size
260 13643 50       17787 if ($generic_data_type ne 'DateTime') {
261 13643 50 66     20788 if ( defined($data_length) and ($data_length < length($value)) ) {
262 0         0 push @tags,
263             UR::Object::Tag->create(
264             type => 'invalid',
265             properties => [$property_name],
266             desc => sprintf('Value too long (%s of %s has length of %d and should be <= %d).',
267             $property_name,
268             $self->$property_name,
269             length($value),
270             $data_length)
271             );
272             }
273             }
274              
275             # Check valid values if there is an explicit list
276 13643 100       22299 if (my $constraints = $property_metadata->valid_values) {
277 1073         1101 my $valid = 0;
278 1073         1729 for my $valid_value (@$constraints) {
279 266     266   409413 no warnings; # undef == ''
  266         446  
  266         280241  
280 1213 100       2175 if ($value eq $valid_value) {
281 1067         970 $valid = 1;
282 1067         1245 last;
283             }
284             }
285 1073 100       1805 unless ($valid) {
286             # undef is a valid value in the constraints list
287 6 50       7 my $value_list = join(', ',map { defined($_) ? $_ : '' } @$constraints);
  24         39  
288 6         40 push @tags,
289             UR::Object::Tag->create(
290             type => 'invalid',
291             properties => [$property_name],
292             desc => sprintf(
293             'The value %s is not in the list of valid values for %s. Valid values are: %s',
294             $value,
295             $property_name,
296             $value_list
297             )
298             );
299             }
300             }
301              
302             # Check FK if it is easy to do.
303             # TODO: This is a heavy weight check, and is disabled for performance reasons.
304             # Ideally we'd check a foreign key value _if_ it was changed only, since
305             # saved foreign keys presumably could not have been save if they were invalid.
306 13643         17153 if (0) {
307             my $r_class;
308             unless ($r_class->get(id => $value)) {
309             push @tags, UR::Object::Tag->create (
310             type => 'invalid',
311             properties => [$property_name],
312             desc => "$value does not reference a valid " . $r_class . '.'
313             );
314             }
315             }
316             }
317              
318 2995         8546 return @tags;
319             }
320              
321             # Standard API for working with UR fixtures
322             # boolean expressions
323             # sets
324             # iterators
325             # views
326             # mock objects
327              
328             sub define_boolexpr {
329 1582 50   1582 1 44586 if (ref($_[0])) {
330 0         0 my $class = ref(shift);
331 0         0 return UR::BoolExpr->resolve($class,@_);
332             }
333             else {
334 1582         5305 return UR::BoolExpr->resolve(@_);
335             }
336             }
337              
338             sub define_set {
339 26     26 1 4585 my $class = shift;
340 26   33     134 $class = ref($class) || $class;
341 26         135 my $rule = UR::BoolExpr->resolve_normalized($class,@_);
342 26         93 my $flattened_rule = $rule->flatten_hard_refs();
343 26         68 my $set_class = $class . "::Set";
344 26         59 return $set_class->get($flattened_rule->id);
345             }
346              
347             sub add_observer {
348 108     108 1 15233 my $self = shift;
349 108         311 my %params = @_;
350              
351 108 100       311 if (ref($self)) {
352 60         208 $params{subject_id} = $self->id;
353             }
354 108         383 my $observer = UR::Observer->create(
355             subject_class_name => $self->class,
356             %params,
357             );
358 108 50       297 unless ($observer) {
359 0         0 $self->error_message(
360             "Failed to create observer: "
361             . UR::Observer->error_message
362             );
363 0         0 return;
364             }
365 108         448 return $observer;
366             }
367              
368             sub remove_observers {
369 1     1 0 279 my $self = shift;
370 1         2 my %params = @_;
371              
372 1         2 my $aspect = delete $params{'aspect'};
373 1         3 my $callback = delete $params{'callback'};
374 1 50       3 if (%params) {
375 0         0 Carp::croak('Unrecognized parameters for observer removal: '
376             . Data::Dumper::Dumper(\%params)
377             . "Expected 'aspect' and 'callback'");
378             }
379              
380 1         4 my %args = ( subject_class_name => $self->class );
381 1 50       5 $args{'subject_id'} = $self->id if (ref $self);
382 1 50       3 $args{'aspect'} = $aspect if (defined $aspect);
383 1 50       2 $args{'callback'} = $callback if (defined $callback);
384 1         4 my @observers = UR::Observer->get(%args);
385              
386 1         6 $_->delete foreach @observers;
387 1         6 return @observers;
388             }
389              
390             sub create_iterator {
391 65     65 1 28221 my $class = shift;
392              
393             # old syntax = create_iterator(where => [param_a => A, param_b => B])
394 65 100       216 if (@_ > 1) {
395 42         133 my %params = @_;
396 42 50       158 if (exists $params{'where'}) {
397 0         0 Carp::carp('create_iterator called with old syntax create_iterator(where => \@params) should be called as create_iterator(@params)');
398 0         0 @_ = $params{'where'};
399             }
400             }
401              
402             # new syntax, same as get() = create_iterator($bx) or create_iterator(param_a => A, param_b => B)
403 65         91 my $filter;
404 65 100 66     463 if (Scalar::Util::blessed($_[0]) && $_[0]->isa('UR::BoolExpr')) {
405 15         28 $filter = $_[0];
406             } else {
407 50         226 $filter = UR::BoolExpr->resolve($class, @_)
408             }
409              
410 65         398 my $iterator = UR::Object::Iterator->create_for_filter_rule($filter);
411 65 50       304 unless ($iterator) {
412 0         0 $class->error_message(UR::Object::Iterator->error_message);
413 0         0 return;
414             }
415              
416 65         214 return $iterator;
417             }
418              
419             sub create_view {
420 51     51 0 2021 my $self = shift;
421 51         160 my $class = $self->class;
422             # this will auto-subclass into ${class}::View::${perspective}::${toolkit},
423             # using $class or some parent class of $class
424 51         305 my $view = UR::Object::View->create(
425             subject_class_name => $class,
426             perspective => "default",
427             @_
428             );
429              
430 51 50       164 unless ($view) {
431 0         0 $self->error_message("Error creating view: " . UR::Object::View->error_message);
432 0         0 return;
433             }
434              
435 51 100       135 if (ref($self)) {
436 7         47 $view->subject($self);
437             }
438              
439 51         183 return $view;
440             }
441              
442             sub create_mock {
443 0     0 1 0 my $class = shift;
444 0         0 my %params = @_;
445              
446 0         0 require Test::MockObject;
447              
448 0         0 my $self = Test::MockObject->new();
449 0         0 my $subject_class_object = $class->__meta__;
450 0         0 for my $class_object ($subject_class_object,$subject_class_object->ancestry_class_metas) {
451 0         0 for my $property ($class_object->direct_property_metas) {
452 0         0 my $property_name = $property->property_name;
453 0 0 0     0 if (($property->is_delegated || $property->is_optional) && !exists($params{$property_name})) {
      0        
454 0         0 next;
455             }
456 0 0 0     0 if ($property->is_mutable || $property->is_calculated || $property->is_delegated) {
      0        
457             my $sub = sub {
458 0     0   0 my $self = shift;
459 0 0       0 if (@_) {
460 0 0       0 if ($property->is_many) {
461 0         0 $self->{'_'. $property_name} = @_;
462             } else {
463 0         0 $self->{'_'. $property_name} = shift;
464             }
465             }
466 0         0 return $self->{'_'. $property_name};
467 0         0 };
468 0         0 $self->mock($property_name, $sub);
469 0 0       0 if ($property->is_optional) {
470 0 0       0 if (exists($params{$property_name})) {
471 0         0 $self->$property_name($params{$property_name});
472             }
473             } else {
474 0 0       0 unless (exists($params{$property_name})) {
475 0 0       0 if (defined($property->default_value)) {
476 0         0 $params{$property_name} = $property->default_value;
477             } else {
478 0 0       0 unless ($property->is_calculated) {
479 0         0 Carp::croak("Failed to provide value for required mutable property '$property_name'");
480             }
481             }
482             }
483 0         0 $self->$property_name($params{$property_name});
484             }
485             } else {
486 0 0       0 unless (exists($params{$property_name})) {
487 0 0       0 if (defined($property->default_value)) {
488 0         0 $params{$property_name} = $property->default_value;
489             } else {
490 0         0 Carp::croak("Failed to provide value for required property '$property_name'");
491             }
492             }
493 0 0       0 if ($property->is_many) {
494 0         0 $self->set_list($property_name,$params{$property_name});
495             } else {
496 0         0 $self->set_always($property_name,$params{$property_name});
497             }
498             }
499             }
500             }
501 0         0 my @classes = ($class, $subject_class_object->ancestry_class_names);
502 0         0 $self->set_isa(@classes);
503 0         0 $UR::Context::all_objects_loaded->{$class}->{$self->id} = $self;
504 0         0 return $self;
505             }
506              
507             # Typically only used internally by UR except when debugging.
508              
509             sub __changes__ {
510             # Return a list of changes present on the object _directly_.
511             # This is really only useful internally because the boundary of the object
512             # is internal/subjective.
513              
514 242068     242068   165077 my $self = shift;
515              
516             # performance optimization
517 242068 100       621662 return unless $self->{_change_count};
518              
519 927         1976 my $meta = $self->__meta__;
520 927 50       1753 if (ref($meta) eq 'UR::DeletedRef') {
521 0         0 print Data::Dumper::Dumper($self,$meta);
522 0         0 Carp::confess("Meta is deleted for object requesting changes: $self\n");
523             }
524 927 50 66     2289 if (!$meta->is_transactional and !$meta->is_meta_meta) {
525 209         325 return;
526             }
527              
528 718   66     1992 my $orig = $self->{db_saved_uncommitted} || $self->{db_committed};
529              
530 718         582 my %prop_metas;
531             my $prop_is_changed = sub {
532 345     345   308 my $prop_name = shift;
533 345   66     1144 my $property_meta = $prop_metas{$prop_name} ||= $meta->property_meta_for_name($prop_name);
534 266     266   1478 no warnings 'uninitialized';
  266         434  
  266         32159  
535 345   66     1281 return ($orig->{$prop_name} ne $self->{$prop_name})
536             &&
537             ($self->can($prop_name) and ! UR::Object->can($prop_name))
538             &&
539             defined($property_meta)
540             &&
541             (! $property_meta->is_transient)
542             ;
543 718         2272 };
544              
545 718 100       1200 unless (wantarray) {
546             # scalar context only cares if there are any changes or not
547 617 100       850 if (@_) {
548 24         41 foreach (@_) {
549 26 100       46 return 1 if $prop_is_changed->($_);
550             }
551 3         28 return '';
552             } else {
553             return ($self->{__defined} and $self->{_change_count} == 1)
554             ? ''
555 593 100 100     3360 : $self->{_change_count};
556             }
557             }
558              
559 266     266   1207 no warnings;
  266         457  
  266         322848  
560 101         122 my @changed;
561 101 50       194 if ($orig) {
562 101         272 my $class_name = $meta->class_name;
563             @changed =
564 319         601 grep { $prop_is_changed->($_) }
565 101 50       356 grep { $_ }
  319         349  
566             @_ ? (@_) : keys(%$orig);
567             }
568             else {
569 0         0 @changed = $meta->all_property_names
570             }
571              
572             return map {
573 101         336 UR::Object::Tag->create
  75         360  
574             (
575             type => 'changed',
576             properties => [$_]
577             )
578             } @changed;
579             }
580              
581              
582             sub _changed_property_names {
583 47     47   72 my $self = shift;
584              
585 47         79 my @changes = $self->__changes__;
586 47         90 my %changed_properties;
587 47         88 foreach my $change ( @changes ) {
588 50 50       1036 next unless ($change->type eq 'changed');
589 50         689 $changed_properties{$_} = 1 foreach $change->properties;
590             }
591 47         220 return keys %changed_properties;
592             }
593              
594             sub __signal_change__ {
595             # all mutable property accessors ("setters") call this method to tell the
596             # current context about a state change.
597 232556     232556   554263 $UR::Context::current->add_change_to_transaction_log(@_);
598 232556         424840 $UR::Context::current->send_notification_to_observers(@_);
599             }
600              
601             # send notifications that aren't state changes to observers
602             sub __signal_observers__ {
603 67391     67391   129686 $UR::Context::current->send_notification_to_observers(@_);
604             }
605              
606             sub __define__ {
607             # This is used internally to "virtually load" things.
608              
609             # Simply assert they already existed externally, and act as though they were just loaded...
610             # It is used for classes defined in the source code (which is the default) by the "class {}" magic
611             # instead of in some database, as we'd do for regular objects. It is also used by some test cases.
612 95883 100 100 95883   294738 if ($UR::initialized and $_[0] ne 'UR::Object::Property') {
613             # the nornal implementation has all create() features
614 504         641 my $self;
615 504         566 do {
616 504         842 local $UR::Context::construction_method = '__define__';
617 504         2207 $self = $UR::Context::current->create_entity(@_);
618             };
619 504 50       1340 return unless $self;
620 504         2651 $self->{db_committed} = { %$self };
621 504         981 $self->{'__defined'} = 1;
622 504         1777 $self->__signal_change__("load");
623 504         1258 return $self;
624             }
625             else {
626             # used during boostrapping
627 95379         88086 my $class = shift;
628 95379         201128 my $class_meta = $class->__meta__;
629 95379 50       217939 if (my $method_name = $class_meta->sub_classification_method_name) {
630 0         0 my($rule, %extra) = UR::BoolExpr->resolve_normalized($class, @_);
631 0         0 my $sub_class_name = $class->$method_name(@_);
632 0 0       0 if ($sub_class_name ne $class) {
633             # delegate to the sub-class to create the object
634 0         0 return $sub_class_name->__define__(@_);
635             }
636             }
637              
638 95379         262094 my $self = $UR::Context::current->_construct_object($class, @_);
639 95379 50       148709 return unless $self;
640 95379         838218 $self->{db_committed} = { %$self };
641 95379         212341 $self->__signal_change__("load");
642 95379         187103 return $self;
643             }
644             }
645              
646             sub __extend_namespace__ {
647             # A class Foo can implement this method to have a chance to auto-define Foo::Bar
648             # TODO: make a Class::Autouse::ExtendNamespace Foo => sub { } to handle this.
649             # Right now, UR::ModuleLoader will try it after "use".
650 2065     2065   2672 my $class = shift;
651 2065         2292 my $ext = shift;
652 2065         4230 my $class_meta = $class->__meta__;
653 2065         10769 return $class_meta->generate_support_class_for_extension($ext);
654             }
655              
656             # Handling of references within the current process
657              
658             sub is_weakened {
659 4370     4370 1 2693 my $self = shift;
660 4370   66     11041 return (exists $self->{__weakened} && $self->{__weakened});
661             }
662              
663             sub __weaken__ {
664             # Mark this object as unloadable by the object cache pruner.
665             # If the class has a data source, then a weakened object is dropped
666             # at the first opportunity, reguardless of its __get_serial number.
667             # For classes without a data source, then it will be dropped according to
668             # the normal rules w/r/t the __get_serial (classes without data sources
669             # normally are never dropped by the pruner)
670 12     12   17 my $self = $_[0];
671 12         22 delete $self->{'__strengthened'};
672 12         44 $self->{'__weakened'} = 1;
673             }
674              
675             sub is_strengthened {
676 2197     2197 1 1300 my $self = shift;
677 2197   33     4421 return (exists $self->{__strengthened} && $self->{__strengthened});
678             }
679              
680             sub __strengthen__ {
681             # Indicate this object should never be unloaded by the object cache pruner
682             # or AutoUnloadPool
683 0     0   0 my $self = $_[0];
684 0         0 delete $self->{'__weakened'};
685 0         0 $self->{'__strengthened'} = 1;
686             }
687              
688             sub is_prunable {
689 2197     2197 0 1416 my $self = shift;
690 2197 50       2157 return 0 if $self->is_strengthened;
691 2197 50       2395 return 1 if $self->is_weakened;
692 2197 100       3272 return 0 if $self->__meta__->is_meta;
693 2188 50 66     4394 return 0 if $self->{__get_serial} && $self->__changes__ && @{[$self->__changes__]};
  0   33     0  
694 2188         8278 return 1;
695             }
696              
697              
698             sub __rollback__ {
699 967     967   961 my $self = shift;
700              
701 967   66     2935 my $saved = $self->{db_saved_uncommitted} || $self->{db_committed};
702 967 100       1643 unless ($saved) {
703 27         59 return UR::Object::delete($self);
704             }
705              
706 940         2961 my $meta = $self->__meta__;
707              
708             my $should_rollback = sub {
709 46834     46834   29313 my $property_meta = shift;
710             return ! (
711 46834   66     57950 defined $property_meta->is_id
712             || ! defined $property_meta->column_name
713             || $property_meta->is_delegated
714             || $property_meta->is_legacy_eav
715             || ! $property_meta->is_mutable
716             || $property_meta->is_transient
717             || $property_meta->is_constant
718             );
719 940         3390 };
720             my @rollback_property_names =
721 329         554 map { $_->property_name }
722 46834         40358 grep { $should_rollback->($_) }
723 940         2978 map { $meta->property_meta_for_name($_) }
  46834         70936  
724             $meta->all_property_names;
725              
726             # Existing object. Undo all changes since last sync, or since load
727             # occurred when there have been no syncs.
728 940         5094 foreach my $property_name ( @rollback_property_names ) {
729 329         524 $self->__rollback_property__($property_name);
730             }
731              
732 940         1566 delete $self->{'_change_count'};
733              
734 940         4728 return $self;
735             }
736              
737              
738             sub __rollback_property__ {
739 329     329   300 my ($self, $property_name) = @_;
740 329   66     594 my $saved = $self->{db_saved_uncommitted} || $self->{db_committed};
741 329 50       451 unless ($saved) {
742 0         0 Carp::croak(qq(Cannot rollback property '$property_name' because it has no saved state));
743             }
744 329         666 my $saved_value = UR::Context->current->value_for_object_property_in_underlying_context($self, $property_name);
745 329         857 return $self->$property_name($saved_value);
746             }
747              
748              
749             sub DESTROY {
750             # Handle weak references in the object cache.
751 411     411   6480 my $obj = shift;
752              
753             # objects_may_go_out_of_scope will be true if either light_cache is on, or
754             # the cache_size_highwater mark is a valid value
755 411         724 my($class, $id) = (ref($obj), $obj->{id});
756              
757 411 100 66     2399 if (isweak($UR::Context::all_objects_loaded->{$class}{$id})
    50          
758             and
759             refaddr($UR::Context::all_objects_loaded->{$class}{$id}) == refaddr($obj)
760             ) {
761             # This object was dropped by the cache pruner or an AutoUnloadPool
762 295 50       470 if (() = $obj->__changes__) {
763 0 0       0 print STDERR "MEM DESTROY keeping changed object $class id $id\n" if $ENV{'UR_DEBUG_OBJECT_RELEASE'};
764 0         0 $obj->_save_object_from_destruction();
765 0         0 return;
766             } else {
767 295 50       460 print STDERR "MEM DESTROY object $obj class $class if $id\n" if $ENV{'UR_DEBUG_OBJECT_RELEASE'};
768 295         591 $obj->unload();
769 295         679 return $obj->SUPER::DESTROY();
770             }
771             }
772             elsif (UR::Context::objects_may_go_out_of_scope()) {
773 0         0 my $obj_from_cache = delete $UR::Context::all_objects_loaded->{$class}{$id};
774 0 0 0     0 if ($obj->__meta__->is_meta_meta or @{[$obj->__changes__]}) {
  0         0  
775 0 0       0 die "Object found in all_objects_loaded does not match destroyed ref/id! $obj/$id!" unless refaddr($obj) == refaddr($obj_from_cache);
776 0         0 $obj->_save_object_from_destruction();
777 0 0       0 print "MEM DESTROY Keeping infrastructure/changed object $obj class $class if $id\n" if $ENV{'UR_DEBUG_OBJECT_RELEASE'};
778 0         0 return;
779             }
780             else {
781 0 0       0 if ($ENV{'UR_DEBUG_OBJECT_RELEASE'}) {
782 0         0 print STDERR "MEM DESTROY object $obj class $class id $id\n";
783             }
784 0         0 $obj->unload();
785 0         0 return $obj->SUPER::DESTROY();
786             }
787             }
788             else {
789 116 50       301 if ($ENV{'UR_DEBUG_OBJECT_RELEASE'}) {
790 0         0 print STDERR "MEM DESTROY object $obj class $class id $id\n";
791             }
792 116         580 $obj->SUPER::DESTROY();
793             }
794             };
795              
796             sub _save_object_from_destruction {
797 0     0     my $obj = shift;
798 0           my($class, $id) = (ref($obj), $obj->{id});
799 0           $UR::Context::all_objects_loaded->{$class}{$id} = $obj;
800             }
801              
802             END {
803             # Turn off monitoring of the DESTROY handler at application exit.
804             # setting the typeglob to undef does not work. -sms
805 266     266   1718506 delete $UR::Object::{DESTROY};
806             };
807              
808             # This module implements the deprecated parts of the UR::Object API
809             require UR::ObjectDeprecated;
810              
811             1;
812              
813             =pod
814              
815             =head1 NAME
816              
817             UR::Object - transactional, queryable, process-independent entities
818              
819             =head1 SYNOPSIS
820              
821             Create a new object in the current context, and return it:
822              
823             $elmo = Acme::Puppet->create(
824             name => 'Elmo',
825             father => $ernie,
826             mother => $bigbird,
827             jobs => [$dance, $sing],
828             favorite_color => 'red',
829             );
830              
831             Plain accessors work in the typial fashion:
832              
833             $color = $elmo->favorite_color();
834              
835             Changes occur in a transaction in the current context:
836              
837             $elmo->favorite_color('blue');
838              
839             Non-scalar (has_many) properties have a variety of accessors:
840              
841             @jobs = $elmo->jobs();
842             $jobs = $elmo->job_arrayref();
843             $set = $elmo->job_set();
844             $iter = $elmo->job_iterator();
845             $job = $elmo->add_job($snore);
846             $success = $elmo->remove_job($sing);
847              
848             Query the current context to find objects:
849              
850             $existing_obj = Acme::Puppet->get(name => 'Elmo');
851             # same reference as $existing_obj
852              
853             @existing_objs = Acme::Puppet->get(
854             favorite_color => ['red','yellow'],
855             );
856             # this will not get elmo because his favorite color is now blue
857              
858             @existing_objs = Acme::Puppet->get(job => $snore);
859             # this will return $elmo along with other puppets that snore,
860             # though we haven't saved the change yet..
861              
862             Save our changes:
863              
864             UR::Context->current->commit;
865              
866             Too many puppets...:
867              
868             $elmo->delete;
869              
870             $elmo->play; # this will throw an exception now
871              
872             $elmo = Acme::Puppet->get(name => 'Elmo'); # this returns nothing now
873              
874             Just kidding:
875              
876             UR::Context->current->rollback; # not a database rollback, an in-memory undo
877              
878             All is well:
879              
880             $elmo = Acme::Puppet->get(name => 'Elmo'); # back again!
881              
882             =head1 DESCRIPTION
883              
884             UR::Objects are transactional, queryable, representations of entities, built to maintain
885             separation between the physical reference in a program, and the logical entity the
886             reference represents, using a well-defined interface.
887              
888             UR uses that separation to automatically handle I/O. It provides a query API,
889             and manages the difference between the state of entities in the application,
890             and their state in external persistence systems. It aims to do so transparently,
891             keeping I/O logic orthogonally to "business logic", and hopefully making code
892             around I/O unnecessary to write at all for most programs.
893              
894             Rather than explicitly constructing and serializing/deserializing objects, the
895             application layer just requests objects from the current "context", according to
896             their characteristics. The context manages database connections, object state
897             changes, references, relationships, in-memory transactions, queries and caching in
898             tunable ways.
899              
900             Accessors dynamically fabricate references lazily, as needed through the same
901             query API, so objects work as the developer would traditionally expect in
902             most cases. The goal of UR::Object is that your application doesn't have to do
903             data management. Just ask for what you want, use it, and let it go.
904              
905             UR::Objects support full reflection and meta-programming. Its meta-object
906             layer is fully self-bootstrapping (most classes of which UR is composed are
907             themselves UR::Objects), so the class data can introspect itself,
908             such that even classes can be created within transactions and discarded.
909              
910             =head1 INHERITANCE
911              
912             UR::ModuleBase Basic error, warning, and status messages for modules in UR.
913             UR::Object This class - general OO transactional OO features
914              
915             =head1 WRITING CLASSES
916              
917             See L for a narrative explanation of how to write clases.
918              
919             For a complete reference see L.
920              
921             For the meta-object API see L.
922              
923             A simple example, declaring the class used above:
924              
925             class Acme::Puppet {
926             id_by => 'name',
927             has_optional => [
928             father => { is => 'Acme::Puppet' },
929             mother => { is => 'Acme::Puppet' },
930             jobs => { is => 'Acme::Job', is_many => 1 },
931             ]
932             };
933              
934             You can also declare the same API, but specifying additional internal details to make
935             database mapping occur the way you'd like:
936              
937             class Acme::Puppet {
938             id_by => 'name',
939             has_optional => [
940             father => { is => 'Acme::Puppet', id_by => 'father_id' },
941             mother => { is => 'Acme::Puppet', id_by => 'mother_id' },
942             },
943             has_many_optional => [
944             job_assignments => { is => 'Acme::PuppetJob', im_its => 'puppet' },
945             jobs => { is => 'Acme::Job', via => 'job_assignments', to => 'job' },
946             ]
947             };
948              
949              
950             =head1 CONSTRUCTING OBJECTS
951              
952             New objects are returned by create() and get(), which delegate to the current
953             context for all object construction.
954              
955             The create() method will always create something new or will return undef if
956             the identity is already known to be in use.
957              
958             The get() method lets the context internally decide whether to return a cached
959             reference for the specified logical entities or to construct new objects
960             by loading data from the outside.
961              
962             =head1 METHODS
963              
964             The examples below use $obj where an actual object reference is required,
965             and SomeClass where the class name can be used. In some cases the
966             example in the synopsisis is continued for deeper illustration.
967              
968             =head2 Base API
969              
970             =over 4
971              
972             =item get
973              
974             $obj = SomeClass->get($id);
975             $obj = SomeClass->get(property1 => value1, ...);
976             @obj = SomeClass->get(property1 => value1, ...);
977             @obj = SomeClass->get('property1 operator1' => value1, ...);
978              
979             Query the current context for objects.
980              
981             It turns the passed-in parameters into a L and returns all
982             objects of the given class which match. The current context determines
983             whether the request can be fulfilled without external queries. Data
984             is loaded from underlying database(s) lazliy as needed to fulfuill the
985             request.
986              
987             In the simplest case of requesting an object by id which is cached, the
988             call to get() is an immediate hash lookup, and is very fast.
989              
990             See L, or look at L, L,
991             and L for details.
992              
993             If called in scalar context and more than one object matches the given
994             parameters, get() will raise an exception through C.
995              
996             =item create
997              
998             $obj = SomeClass->create(
999             property1 => $value1,
1000             properties2 => \@values2,
1001             );
1002              
1003             Create a new entity in the current context, and return a reference to it.
1004              
1005             The only required property to create an object is the "id",
1006             and that is only required for objects which do not autogenerate their
1007             own ids. This requirement may be overridden in subclasses to be
1008             more restrictive.
1009              
1010             If entities of this type persist in an underlying context, the entity will
1011             not appear there until commit. (i.e. no insert is done until just before
1012             a real database commit) The object in question does not need to pass its own
1013             constraints when initially created, but must be fully valid before the
1014             transaction which created it commits.
1015              
1016             =item delete
1017              
1018             $obj->delete
1019              
1020             Deletes an object in the current context.
1021              
1022             The $obj reference will be garbage collected at the discretion of the Perl interpreter as soon as possible.
1023             Any attempt to use the reference after delete() is called will result in an exception.
1024              
1025             If the represented entity was loaded from the parent context (i.e. persistent database objects),
1026             it will not be deleted from that context (the database) until commit is called. The commit call
1027             will do both the delete and the commit, presuming the complete save works across all involved
1028             data sources.
1029              
1030             Should the transaction roll-back, the deleted object will be re-created in the current context,
1031             and a fresh reference will later be returnable by get(). See the documentation on L
1032             for details on how deleted objects are rememberd and removed later from the database, and how
1033             deleted objects are re-constructed on STM rollback.
1034              
1035             =item copy
1036              
1037             $obj->copy(%overrides)
1038              
1039             Copies the existing C<$obj> by copying the values of all direct properties,
1040             except for ID properties, to a newly created object of the same type. A list
1041             of params and values may be provided as overrides to the existing values or to
1042             specify an ID.
1043              
1044             =item class
1045              
1046             $class_name = $obj->class;
1047             $class_name = SomeClass->class;
1048              
1049             Returns the name of the class of the object in question. See __meta__ below
1050             for the class meta-object.
1051              
1052             =item id
1053              
1054             $id = $obj->id;
1055              
1056             The unique identifier of the object within its class.
1057              
1058             For database-tracked entities this is the primary key value, or a composite
1059             blob containing the primary key values for multi-column primary keys.
1060              
1061             For regular objects private to the process, the default id embeds the
1062             hostname, process ID, and a timestamp to uniquely identify the
1063             UR::Context::Process object which is its final home.
1064              
1065             When inheritance is involved beneath UR::Object, the 'id' may identify the object
1066             within the super-class as well. It is also possible for an object to have a
1067             different id upon sub-classification.
1068              
1069              
1070             =back
1071              
1072             =head2 Accessors
1073              
1074             Every relationship declared in the class definition results in at least one
1075             accesor being generated for the class in question.
1076              
1077             Identity properties are read-only, while non-identity properties are read-write
1078             unless is_mutable is explicitly set to false.
1079              
1080             Assigning an invalid value is allowed temporarily, but the current transaction
1081             will be in an invalid state until corrected, and will not be commitable.
1082              
1083             The return value of an the accessor when it mutates the object is
1084             the value of the property after the mutation has occurred.
1085              
1086              
1087             =head3 Single-value property accessors:
1088              
1089             By default, properties are expected to return a single value.
1090              
1091             =over 4
1092              
1093             =item NAME
1094              
1095             Regular accessors have the same name as the property, as declared, and also work
1096             as mutators as is commonly expected:
1097              
1098             $value = $obj->property_name;
1099             $obj->property_name($new_value);
1100              
1101             When the property is declared with id_by instead of recording the refereince, it
1102             records the id of the object automatically, such that both will return different
1103             values after either changes.
1104              
1105             =back
1106              
1107             =head3 Muli-value property accessors:
1108              
1109             When a property is declared with the "is_many" flag, a variety of accessors are made
1110             available on the object. See C for more details
1111             on the ways to declare relationships between objects when writing classes.
1112              
1113             Using the example from the synopsis:
1114              
1115             =over 4
1116              
1117             =item NAMEs (the property name pluralized)
1118              
1119             A "has_many" relationship is declared using the plural form of the relationship name.
1120             An accessor returning the list of property values is generated for the class. It
1121             is usable with or without additional filters:
1122              
1123             @jobs = $elmo->jobs();
1124             @fun_jobs = $elmo->jobs(is_fun => 1);
1125              
1126             The singular name is used for the remainder of the accessors...
1127              
1128             =item NAME (the property name in singular form)
1129              
1130             Returns one item from the group, which must be specified in parameters. If more
1131             than one item is matched, an exception is thrown via die():
1132              
1133             $job = $elmo->job(name => 'Sing');
1134              
1135             $job = $elmo->job(is_fun => 1);
1136             # die: too many things are fun for Elmo
1137              
1138             =item NAME_list
1139              
1140             The default accessor is available as *_list. Usable with or without additional filters:
1141              
1142             @jobs = $elmo->job_list();
1143             @fun_jobs = $elmo_>job_list(is_fun => 1);
1144              
1145              
1146             =item NAME_set
1147              
1148             Return a L value representing the values with *_set:
1149              
1150             $set = $elmo->job_set();
1151             $set = $elmo->job_set(is_hard => 1);
1152              
1153              
1154             =item NAME_iterator
1155              
1156             Create a new iterator for the set of property values with *_iterator:
1157              
1158             $iter = $elmo->job_iterator();
1159             $iter = $elmo->job_iterator(is_fun => 1, -order_by => ['name]);
1160             while($obj = $iter->next()) { ... }
1161              
1162             =item add_NAME
1163              
1164             Add an item to the set of values with add_*:
1165              
1166             $added = $elmo->add_job($snore);
1167              
1168             A variation of the above will construt the item and add it at once.
1169             This second form of add_* automatically would identify that the line items
1170             also reference the order, and establish the correct converse relationship
1171             automatically.
1172              
1173             @lines = $order->lines;
1174             # 2 lines, for instance
1175              
1176             $line = $order->add_line(
1177             product => $p,
1178             quantity => $q,
1179             );
1180             print $line->num;
1181             # 3, if the line item has a multi-column primary key with auto_increment on the 2nd column called num
1182              
1183             =item remove_NAME
1184              
1185             Items can be removed from the assigned group in a way symetrical with how they are added:
1186              
1187             $removed = $elmo->remove_job($sing);
1188              
1189             =back
1190              
1191             =head2 Extended API
1192              
1193             These methods are available on any class defined by UR. They
1194             are convenience methods around L, L,
1195             L, L, L
1196             and L.
1197              
1198             =over 4
1199              
1200             =item create_iterator
1201              
1202             $iter = SomeClass->create_iterator(
1203             property1 => $explicit_value,
1204             property2 => \@my_in_clause,
1205             'property3 like' => 'some_pattern_with_%_as_wildcard',
1206             'property4 between' => [$low,$high],
1207             );
1208              
1209             while (my $obj = $iter->next) {
1210             ...
1211             }
1212              
1213             Takes the same sort of parameters as get(), but returns a L
1214             for the matching objects.
1215              
1216             The next() method will return one object from the resulting set each time it is
1217             called, and undef when the results have been exhausted.
1218              
1219             C instances are normal object references in the current
1220             process, not context-oriented UR::Objects. They vanish upon dereference,
1221             and cannot be retrieved by querying the context.
1222              
1223             When using an iterator, the system attempts to return objects matching the params
1224             at the time the iterator is created, even if those objects do not match the
1225             params at the time they are returned from next(). Consider this case:
1226              
1227             # many objects in the DB match this
1228             my $iter = SomeClass->create_iterator(job => 'cleaner');
1229              
1230             my $an_obj = SomeClass->get(job => 'cleaner', id => 1);
1231             $an_obj->job('messer-upper'); # This no longer matches the iterator's params
1232              
1233             my @iter_objs;
1234             while (my $o = $iter->next) {
1235             push @iter_objs, $o;
1236             }
1237              
1238             At the end, @iter_objs will contain several objects, including the object with id 1,
1239             even though its job is no longer 'cleaner'. However, if an object matching the
1240             iterator's params is deleted between the time the iterator is created and the time
1241             next() would return that object, then next() will throw an exception.
1242              
1243             =item define_set
1244              
1245             $set = SomeClass->define_set(
1246             property1 => $explicit_value,
1247             property2 => \@my_in_clause,
1248             'property3 like' => 'some_pattern_with_%_as_wildcard',
1249             'property4 between' => [$low,$high],
1250             );
1251              
1252             @subsets = $set->group_by('property3','property4');
1253              
1254             @some_members = $subsets[0]->members;
1255              
1256             Takes the same sort of parameters as get(), but returns a set object.
1257              
1258             Sets are lazy, and only query underlying databases as much as necessary. At any point
1259             in time the members() method returns all matches to the specified parameters.
1260              
1261             See L for details.
1262              
1263             =item define_boolexpr
1264              
1265             $bx = SomeClass->define_boolexpr(
1266             property1 => $explicit_value,
1267             property2 => \@my_in_clause,
1268             'property3 like' => 'some_pattern_with_%_as_wildcard',
1269             'property4 between' => [$low,$high],
1270             );
1271              
1272             $bx->evaluate($obj1); # true or false?
1273              
1274             Takes the same sort of parameters as get(), but returns a L object.
1275              
1276             The boolean expression can be used to evaluate other objects to see if they match
1277             the given condition. The "id" of the object embeds the complete "where clause",
1278             and as a semi-human-readable blob, such is reconstitutable from it.
1279              
1280             See L for details on how to use this to do advanced work on
1281             defining sets, comparing objects, creating query templates, adding
1282             object constraints, etc.
1283              
1284             =item add_observer
1285              
1286             $o = $obj1->add_observer(
1287             aspect => 'someproperty'
1288             callback => sub { print "change!\n" },
1289             );
1290              
1291             $obj1->property1('new value');
1292              
1293             # observer callback fires....
1294              
1295             $o->delete;
1296              
1297             Adds an observer to an object, monitoring one or more of its properties for changes.
1298              
1299             The specified callback is fired upon property changes which match the observation request.
1300              
1301             See L for details.
1302              
1303             =item create_mock
1304              
1305             $mock = SomeClass->create_mock(
1306             property1 => $value,
1307             method1 => $return_value,
1308             );
1309              
1310             Creates a mock object using using the class meta-data for "SomeClass" via L.
1311              
1312             Useful for test cases.
1313              
1314             =back
1315              
1316             =head2 Meta API
1317              
1318             The following methods allow the application to interrogate UR for information
1319             about the object in question.
1320              
1321             =over 4
1322              
1323             =item __meta__
1324              
1325             $class_obj = $obj->__meta__();
1326              
1327             Returns the class metadata object for the given object's class. Class objects
1328             are from the class L, and hold information about the class'
1329             properties, data source, relationships to other classes, etc.
1330              
1331             =item __extend_namespace__
1332              
1333             package Foo::Bar;
1334              
1335             class Foo::Bar { has => ['stuff','things'] };
1336              
1337             sub __extend_namespace__ {
1338             my $class = shift;
1339             my $ext = shift;
1340             return class {$class . '::' . $ext} { has => ['more'] };
1341             }
1342              
1343             Dynamically generate new classes under a given namespace.
1344             This is called automatically by UR::ModuleLoader when an unidentified class name is used.
1345              
1346             If Foo::Bar::Baz is not a UR class, and this occurs:
1347              
1348             Foo::Bar::Baz->some_method()
1349              
1350             This is called:
1351              
1352             Foo::Bar->__extend_namespace__("Baz")
1353              
1354             If it returns a new class meta, the code will proceed on as though the class
1355             had always existed.
1356              
1357             If Foo::Bar does not exist, the above will be called recursively:
1358              
1359             Foo->__extend_namespace__("Bar")
1360              
1361             If Foo::Bar, whether loaded or generated, cannot extend itself for "Baz",
1362             the loader will go up the tree before giving up. This means a top-level
1363             module could dynamically define classes for any given class name used
1364             under it:
1365              
1366             Foo->__extend_namespace__("Bar::Baz")
1367              
1368             =item __errors__
1369              
1370             @tags = $obj->__errors__()
1371              
1372             Return a list of L values describing the issues which would
1373             prevent a commit in the current transaction.
1374              
1375             The base implementation check the validity of an object by applying any constraints
1376             layed out in the class such as making sure any non-optional properties contain values,
1377             numeric properties contain numeric data, and properties with enumerated values only
1378             contain valid values.
1379              
1380             Sub-classes can override this method to add additional validity checking.
1381              
1382             =item __display_name__
1383              
1384             $text = $obj->__display_name__;
1385             # the class and id of $obj, by default
1386              
1387             $text = $line_item->__display_name__($order);
1388              
1389             Stringifies an object. Some classes may choose to actually overload the stringification operator
1390             with this method. Even if they do not, this method will still attempt to identify this object in
1391             text form. The default returns the class name and id value of the object within a string.
1392              
1393             It can be overridden to do a more nuanced job. The class might also choose to overload the
1394             stringification operator itself with this method, but even if it doesn not the system will
1395             presume this method can be called directly on an object for reasonable stringificaiton.
1396              
1397             =item __context__
1398              
1399             $c = $self->__context__;
1400              
1401             Return the L for the object reference in question.
1402              
1403             In UR, a "context" handles connextions between objects, instead of relying
1404             on having objects directly reference each other. This allows an object
1405             to have a relationship with a large number of other logical entities,
1406             without having a "physical" reference present within the process in question.
1407              
1408             All attempts to resolve non-primitive attribute access go through the context.
1409              
1410             =back
1411              
1412             =head2 Extension API
1413              
1414             These methods are primarily of interest for debugging, for test cases, and internal UR development.
1415              
1416             They are likely to change before the 1.0 release.
1417              
1418             =over 4
1419              
1420             =item __signal_change__
1421              
1422             Called by all mutators to tell the current context about a state change.
1423              
1424             =item __changes__
1425              
1426             @tags = $obj->__changes__()
1427              
1428             @tags = $obj->__changes__('prop1', 'prop2', ...)
1429              
1430             Return a list of changes present on the object _directly_. This is really only
1431             useful internally because the boundary of the object is internal/subjective.
1432             Callers may also request only changes to particular properties.
1433              
1434             Changes to objects' properties are tracked by the system. If an object has been
1435             changed since it was defined or loaded from its external data source, then changed()
1436             will return a list of L objects describing which properties have been
1437             changed.
1438              
1439             Work is in-progress on an API to request the portion of the changes in effect in the
1440             current transaction which would impact the return value of a given list of properties.
1441             This would be directly usable by a view/observer.
1442              
1443             =item __define__
1444              
1445             This is used internally to "virtually load" things. Simply assert they already existed
1446             externally, and act as though they were just loaded... It is used for classes defined in
1447             the source code (which is the default) by the "class {}" magic instead of in some database,
1448             as we'd do for regular objects.
1449              
1450             =item __strengthen__
1451              
1452             $obj->__strengthen__();
1453              
1454             Mark this object as unloadable by the object cache pruner.
1455              
1456             UR objects are normally tracked by the current Context for the life of the
1457             application, but the programmer can specify a limit to cache size, in
1458             which case old, unchanged objects are periodically pruned from the cache.
1459             If strengthen() is called on an object, it will effectively be locked in
1460             the cache, and will not be considered for pruning.
1461              
1462             See L for more information about the pruning mechanism.
1463              
1464             =item is_strengthened
1465              
1466             Check if an object has been stengthened, C<__stengthen__>.
1467              
1468             =item __weaken__
1469              
1470             $obj->__weaken__();
1471              
1472             Give a hint to the object cache pruner that this instance is not going to be used
1473             in the application in the future, and should be removed with preference when
1474             pruning the cache.
1475              
1476             =item is_weakened
1477              
1478             Check if an object has been weakened, C<__weaken__>.
1479              
1480             =item DESTROY
1481              
1482             Perl calls this method on any object before garbage collecting it. It
1483             should never by called by your application explicitly.
1484              
1485             The DESTROY handler is overridden in UR::Object. If you override it in
1486             a subclass, be sure to call $self->SUPER::DESTROY() before exiting your
1487             override, or errors will occur.
1488              
1489             =back
1490              
1491             =head1 ERRORS, WARNINGS and STATUS MESSAGES
1492              
1493             When an error occurs which is "exceptional" the API will throw an exception via die().
1494              
1495             In some cases, when the possibility of failure is "not-exceptional", the method will simply
1496             return false. In scalar context this will be undef. In list context an empty list.
1497              
1498             When there is ambiguity as to whether this is an error or not (get() for instance, might
1499             simply match zero items, ...or fail to understand your parameters), an exception is used.
1500              
1501             =over 4
1502              
1503             =item error_message
1504              
1505             The standard way to convey the error which has occurred is to set ->error_message() on
1506             the object. This will propagate to the class, and through its inheritance. This is
1507             much like DBI's errstr method, which affects the handle on which it was called, its source
1508             handle, and the DBI package itself.
1509              
1510             =item warning_message
1511              
1512             Calls to warning_message also record themselves on the object in question, and its class(es).
1513              
1514             They also emit a standard Perl warn(), which will invoke $SIG{__WARN__};
1515              
1516             =item status_message
1517              
1518             Calls to status_message are also recorded on the object in question. They can be
1519             monitored through hooks, as can the other messages.
1520              
1521             =back
1522              
1523             See L for more information.
1524              
1525             =head1 SEE ALSO
1526              
1527             L, L, L
1528              
1529             L, L, L, L
1530              
1531             L contains additional methods which are deprecated in the API.
1532              
1533             =cut
1534