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 |