File Coverage

lib/UR/Singleton.pm
Criterion Covered Total %
statement 108 116 93.1
branch 36 40 90.0
condition 9 15 60.0
subroutine 36 39 92.3
pod 17 19 89.4
total 206 229 89.9


line stmt bran cond sub pod time code
1              
2             package UR::Singleton;
3              
4 337     337   2483 use strict;
  289         533  
  278         8252  
5 278     284   1087 use warnings;
  273         388  
  269         79039  
6             require UR;
7             our $VERSION = "0.46"; # UR $VERSION;
8              
9             UR::Object::Type->define(
10             class_name => 'UR::Singleton',
11             is => ['UR::Object'],
12             is_abstract => 1,
13             );
14              
15             sub id {
16 15355     15356 1 29258 my $self = shift;
17 15355 100       47758 return (ref $self ? $self->SUPER::id(@_) : $self);
18             }
19              
20             sub _init_subclass {
21 2022     2022   4023 my $class_name = shift;
22 2022         6844 my $class_meta_object = $class_name->__meta__;
23              
24             # Write into the class's namespace the correct singleton overrides
25             # to standard UR::Object methods.
26            
27 2022         2838 my $src;
28 2022 100       16705 if ($class_meta_object->is_abstract) {
29 799         6577 $src = qq|sub ${class_name}::_singleton_object { Carp::confess("${class_name} is an abstract singleton! Select a concrete sub-class.") }|
30             . "\n"
31             . qq|sub ${class_name}::_singleton_class_name { Carp::confess("${class_name} is an abstract singleton! Select a concrete sub-class.") }|
32             . "\n"
33             . qq|sub ${class_name}::_load { shift->_abstract_load(\@_) }|
34             }
35             else {
36 1223         13529 $src = qq|sub ${class_name}::_singleton_object { \$${class_name}::singleton or shift->_concrete_load() }|
37             . "\n"
38             . qq|sub ${class_name}::_singleton_class_name { '${class_name}' }|
39             . "\n"
40             . qq|sub ${class_name}::_load { shift->_concrete_load(\@_) }|
41             . "\n"
42             . qq|sub ${class_name}::get { shift->_concrete_get(\@_) }|
43             . "\n"
44             . qq|sub ${class_name}::is_loaded { shift->_concrete_is_loaded(\@_) }|
45             ;
46             }
47            
48 2022 100   48 1 247607 eval $src;
  48 100   41 1 466  
  41 0   56 1 377  
  56     36 1 537  
  36     2 1 344  
  2     35 1 4  
  35     1 1 65  
  1     0 1 2  
  0     5664 1 0  
  5664     3809 1 20769  
  3809     4512 1 13911  
  4512     4415 1 16933  
  4415     8140 1 16185  
  8140     6918 0 110399  
  6918     5670 0 97378  
  5670     4821   95056  
  4821     1   74177  
  1     0   196  
  0     149      
  0     2      
        2      
        0      
49 2022 50       6679 Carp::confess($@) if $@;
50              
51 2022         8054 return 1;
52             }
53              
54             # Abstract singletons havd a different load() method than concrete ones.
55             # We could do this with forking logic, but since many of the concrete methods
56             # get non-default handling, it's more efficient to do it this way.
57              
58             sub _abstract_load {
59 4478     4478   42157 my $class = shift;
60 3059         21290 my $bx = $class->define_boolexpr(@_);
61 3056         22560 my $id = $bx->value_for_id;
62 750 100       7323 unless (defined $id) {
63 267     270   1424 use Data::Dumper;
  266         353  
  266         68934  
64 18         477 my $params = { $bx->params_list };
65 48         188 Carp::confess("Cannot load a singleton ($class) except by specific identity. " . Dumper($params));
66             }
67 278         2109 my $subclass_name = $class->_resolve_subclass_name_for_id($id);
68 266     266   94653 eval "use $subclass_name";
  266         609  
  266         2662  
  280         19425  
69 280 100       4454 if ($@) {
70 0         0 undef $@;
71 0         0 return;
72             }
73 426         5405 return $subclass_name->get();
74             }
75              
76             # Concrete singletons have overrides to the most basic acccessors to
77             # accomplish class/object duality smoothly.
78              
79             sub _concrete_get {
80 14781 100 66 17561   47429 if (@_ == 1 or (@_ == 2 and $_[0] eq $_[1])) {
      66        
81 14780         216865 my $self = $_[0]->_singleton_object;
82 14778 100       74046 return $self if $self;
83             }
84 1         3 return shift->_concrete_load(@_);
85             }
86              
87             sub _concrete_is_loaded {
88 1174 100 33 3953   4837 if (@_ == 1 or (@_ == 2 and $_[0] eq $_[1])) {
      66        
89            
90 1173         23180 my $self = $_[0]->_singleton_object;
91 1173 100       4050 return $self if $self;
92             }
93 1         6 return shift->SUPER::is_loaded(@_);
94             }
95              
96             sub _concrete_load {
97 1174     1647   2451 my $class = shift;
98              
99 1174   66     5112 $class = ref($class) || $class;
100 266     275   1166 no strict 'refs';
  266         367  
  266         60742  
101 1174         1578 my $varref = \${ $class . "::singleton" };
  1174         4720  
102 1174 100       3197 unless ($$varref) {
103 1173         7155 my $id = $class->_resolve_id_for_subclass_name($class);
104              
105 1173         4341 my $class_object = $class->__meta__;
106 1173         9823 my @prop_names = $class_object->all_property_names;
107 1173         1775 my %default_values;
108 1173         2221 foreach my $prop_name ( @prop_names ) {
109 5364         13150 my $prop = $class_object->property_meta_for_name($prop_name);
110 5364 100       11598 next unless $prop;
111 5364         8366 my $val = $prop->{'default_value'};
112 5364 100       11536 next unless defined $val;
113 2187         4498 $default_values{$prop_name} = $val;
114             }
115            
116 1173         8093 $$varref = $UR::Context::current->_construct_object($class,%default_values, id => $id);
117 1173         17281 $$varref->{db_committed} = { %$$varref };
118 1173         9202 $$varref->__signal_change__("load");
119 1173         5236 Scalar::Util::weaken($$varref);
120             }
121 1174         8814 my $self = $class->_concrete_is_loaded(@_);
122 1174 100       3015 return unless $self;
123 1173 100       6979 unless ($self->init) {
124 0         0 Carp::confess("Failed to initialize singleton $class!");
125             }
126 1173         2442 return $self;
127             }
128              
129             # This is implemented in the singleton to do any post-load processing.
130              
131             sub init {
132 1173     1191 1 3061 return 1;
133             }
134              
135             # All singletons require special deletion logic since they keep a
136             #weakened reference to the singleton.
137              
138             sub delete {
139 1     49 1 400 my $self = shift;
140 1         8 my $class = $self->class;
141 1         5 $self->SUPER::delete();
142 266     270   1142 no strict 'refs';
  266         392  
  266         48447  
143 1 50       1 ${ $class . "::singleton" } = undef if ${ $class . "::singleton" } eq $self;
  1         3  
  1         11  
144 1         7 return $self;
145             }
146              
147             # In most cases, the id is the class name itself, but this is not necessary.
148              
149             sub _resolve_subclass_name_for_id {
150 278     279   599 my $class = shift;
151 278         503 my $id = shift;
152 278         1045 return $id;
153             }
154              
155             sub _resolve_id_for_subclass_name {
156 1173     1176   1926 my $class = shift;
157 1173         1720 my $subclass_name = shift;
158 1173         2150 return $subclass_name;
159             }
160              
161             sub create {
162 1     4 1 364 my $class = shift;
163 1         11 my $bx = $class->define_boolexpr(@_);
164 1         4 my $id = $bx->value_for_id;
165 1 100       3 unless (defined $id) {
166 0         0 Carp::confess("No singleton ID class specified for constructor?");
167             }
168 1         9 my $subclass = $class->_resolve_subclass_name_for_id($id);
169 1         73 eval "use $subclass";
170 1 100       10 unless ($subclass->isa(__PACKAGE__)) {
171 0         0 eval '@' . $subclass . "::ISA = ('" . __PACKAGE__ . "')";
172             }
173            
174 1         7 return $subclass->_concrete_get();
175             }
176              
177              
178             1;
179              
180              
181             =pod
182              
183             =head1 NAME
184              
185             UR::Singleton - Abstract class for implementing singleton objects
186              
187             =head1 SYNOPSIS
188              
189             package MyApp::SomeClass;
190             use UR;
191             class MyApp::SomeClass {
192             is => 'UR::Singleton',
193             has => [
194             foo => { is => 'Number' },
195             ]
196             };
197              
198             $obj = MyApp::SomeClass->get();
199             $obj->foo(1);
200              
201             =head1 DESCRIPTION
202              
203             This class provides the infrastructure for singleton classes. Singletons
204             are classes of which there can only be one instance, and that instance's ID
205             is the class name.
206              
207             If a class inherits from UR::Singleton, it overrides the default
208             implementation of C and C in UR::Object with code that
209             fabricates an appropriate object the first time it's needed.
210              
211             Singletons are most often used as one of the parent classes for data sources
212             within a Namespace. This makes it convienent to refer to them using only
213             their name, as in a class definition.
214              
215             =head1 METHODS
216              
217             =over 4
218              
219             =item _singleton_object
220              
221             $obj = Class::Name->_singleton_object;
222              
223             $obj = $obj->_singleton_object;
224              
225             Returns the object instance whether it is called as a class or object method.
226              
227             =item _singleton_class_name
228              
229             $class_name = Class::Name->_singleton_class_name;
230              
231             $class_name = $obj->_singleton_class_name;
232              
233             Returns the class name whether it is called as a class or object method.
234              
235             =back
236              
237             =head1 SEE ALSO
238              
239             UR::Object
240              
241             =cut