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
|
|
|
|
|
|
|
|