File Coverage

lib/UR/Object/Ghost.pm
Criterion Covered Total %
statement 46 64 71.8
branch 9 14 64.2
condition n/a
subroutine 13 23 56.5
pod 2 9 22.2
total 70 110 63.6


line stmt bran cond sub pod time code
1             #####
2             #
3             # Support "Ghost" objects. These represent deleted items which are not saved.
4             # They are omitted from regular class lists.
5             #
6             #####
7              
8             package UR::Object::Ghost;
9              
10 266     266   1299 use strict;
  266         468  
  266         7816  
11 266     266   1400 use warnings;
  266         418  
  266         13749  
12             require UR;
13             our $VERSION = "0.46"; # UR $VERSION;
14              
15             sub _init_subclass {
16 528     528   1045 my $class_name = pop;
17 266     266   1123 no strict;
  266         452  
  266         5572  
18 266     266   915 no warnings;
  266         415  
  266         128756  
19 528         991 my $live_class_name = $class_name;
20 528         2590 $live_class_name =~ s/::Ghost$//;
21 528     129   2537 *{$class_name ."\:\:class"} = sub { "$class_name" };
  528         4249  
  129         417  
22 528     2   1865 *{$class_name ."\:\:live_class"} = sub { "$live_class_name" };
  528         4471  
  2         16  
23             }
24              
25 0     0 1 0 sub create { Carp::croak('Cannot create() ghosts.') };
26              
27 0     0 1 0 sub delete { Carp::croak('Cannot delete() ghosts.') };
28              
29             sub __rollback__ {
30 2     2   5 my $self = shift;
31              
32             # revive ghost object
33              
34 2     1   15 my $ghost_copy = eval("no strict; no warnings; " . Data::Dumper::Dumper($self));
  1     1   222  
  1     1   1  
  1     1   30  
  1         4  
  1         2  
  1         76  
  1         237  
  1         2  
  1         31  
  1         4  
  1         2  
  1         75  
35 2 50       20 if ($@) {
36 0         0 Carp::confess("Error re-constituting ghost object: $@");
37             }
38 2         3 my($saved_data, $saved_key);
39 2 100       10 if (exists $ghost_copy->{'db_saved_uncommitted'} ) {
    50          
40 1         2 $saved_data = $ghost_copy->{'db_saved_uncommitted'};
41             } elsif (exists $ghost_copy->{'db_committed'} ) {
42 1         2 $saved_data = $ghost_copy->{'db_committed'};
43             } else {
44 0         0 return; # This shouldn't happen?!
45             }
46              
47 2         9 my $new_object = $self->live_class->UR::Object::create(%$saved_data);
48 2 100       8 $new_object->{db_committed} = $ghost_copy->{db_committed} if (exists $ghost_copy->{'db_committed'});
49 2 100       7 $new_object->{db_saved_uncommitted} = $ghost_copy->{db_saved_uncommitted} if (exists $ghost_copy->{'db_saved_uncommitted'});
50 2 50       8 unless ($new_object) {
51 0         0 Carp::confess("Failed to re-constitute $self!");
52             }
53              
54 2         5 return $new_object;
55             }
56              
57             sub _load {
58 0     0   0 shift->is_loaded(@_);
59             }
60              
61              
62             sub unload {
63 0     0 0 0 return;
64             }
65              
66             sub __errors__ {
67 40     40   63 return; # Ghosts are always valid, don't check their properties
68             }
69              
70 0     0 0 0 sub edit_class { undef }
71              
72 0     0 0 0 sub ghost_class { undef }
73              
74 0     0 0 0 sub is_ghost { return 1; }
75              
76             sub live_class
77             {
78 0     0 0 0 my $class = $_[0]->class;
79 0         0 $class =~ s/::Ghost//;
80 0         0 return $class;
81             }
82              
83             my @ghost_changes;
84             sub changed {
85 0 0   0 0 0 @ghost_changes = UR::Object::Tag->create ( type => 'changed', properties => ['id']) unless @ghost_changes;
86 0         0 return @ghost_changes;
87             }
88              
89             sub AUTOSUB
90             {
91             # Delegate to the similar function on the regular class.
92 0     0 0 0 my ($func, $self) = @_;
93 0         0 my $live_class = $self->live_class;
94 0         0 return $live_class->can($func);
95             }
96              
97             1;
98              
99              
100             =pod
101              
102             =head1 NAME
103              
104             UR::Object::Ghost - Abstract class for representing deleted objects not yet committed
105              
106             =head1 SYNOPSIS
107              
108             my $obj = Some::Class->get(1234);
109             $obj->some_method();
110              
111             $obj->delete(); # $obj is now a UR::DeletedRef
112              
113             $ghost = Some::Class::Ghost->get(1234);
114             $ghost->some_method; # Works
115              
116             =head1 DESCRIPTION
117              
118             Ghost objects are a bookkeeping entity for tracking objects which have been
119             loaded from an external data source, deleted within the application, and not
120             yet committed. This implies that they still exist in the external data
121             source. When the Context is committed, the existence of Ghost objects
122             triggers commands to the external data sources to also delete the object(s).
123             When objects are brought into the Context by querying a data source, they
124             are compared against any ghosts that may already exist, and matching objects
125             are not re-loaded or returned to the user from a call to get(). If the
126             user wants to get Ghost objects, they must call get() explicitly on the
127             Ghost class.
128              
129             Each class in the system also has an associated Ghost class, the name of which
130             is formed by tacking '::Ghost' to the name of the regular class. Ghost
131             classes do not have ghosts themselves.
132            
133             Instances of Ghosts are not instantiated with create() directly, they are
134             created as a concequence of deleting a regular object instance. A Ghost
135             can be turned back into a "live" object by re-creating it, or rolling back
136             the transaction it was deleted in.
137              
138             =head1 DEPRECATED
139              
140             Applications will not, and should not, normally interact with Ghosts. The
141             whole Ghost system is scheduled for elimination as we refactor the Context
142             and software transaction framework.
143              
144             =head1 SEE ALSO
145              
146             UR::Object, UR::Object::Type
147              
148             =cut
149