line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package UR; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
# The UR module is itself a "UR::Namespace", besides being the root |
5
|
|
|
|
|
|
|
# module which bootstraps the system. The class definition itself |
6
|
|
|
|
|
|
|
# is made at the bottom of the file. |
7
|
|
|
|
|
|
|
|
8
|
266
|
|
|
266
|
|
2180501
|
use strict; |
|
266
|
|
|
|
|
343
|
|
|
266
|
|
|
|
|
6931
|
|
9
|
266
|
|
|
266
|
|
816
|
use warnings FATAL => 'all'; |
|
266
|
|
|
|
|
954
|
|
|
266
|
|
|
|
|
12718
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Set the version at compile time, since some other modules borrow it. |
12
|
|
|
|
|
|
|
our $VERSION = "0.46"; # UR $VERSION |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Ensure we get detailed errors while starting up. |
15
|
|
|
|
|
|
|
# This is disabled at the bottom of the module. |
16
|
266
|
|
|
266
|
|
915
|
use Carp; |
|
266
|
|
|
|
|
325
|
|
|
266
|
|
|
|
|
18386
|
|
17
|
|
|
|
|
|
|
$SIG{__DIE__} = \&Carp::confess; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# Ensure that, if the application changes directory, we do not |
20
|
|
|
|
|
|
|
# change where we load modules while running. |
21
|
266
|
|
|
266
|
|
983
|
use Cwd; |
|
266
|
|
|
|
|
323
|
|
|
266
|
|
|
|
|
118114
|
|
22
|
|
|
|
|
|
|
my @PERL5LIB = ($ENV{PERL5LIB} ? split(':', $ENV{PERL5LIB}) : ()); |
23
|
|
|
|
|
|
|
for my $dir (@INC, @PERL5LIB) { |
24
|
|
|
|
|
|
|
next unless -d $dir; |
25
|
|
|
|
|
|
|
$dir = Cwd::abs_path($dir) || $dir; |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
$ENV{PERL5LIB} = join(':', @PERL5LIB); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Also need to fix modules that were already loaded, so that when |
30
|
|
|
|
|
|
|
# a namespace is loaded the path will not change out from |
31
|
|
|
|
|
|
|
# underneath it. |
32
|
|
|
|
|
|
|
for my $module (keys %INC) { |
33
|
|
|
|
|
|
|
$INC{$module} = Cwd::abs_path($INC{$module}); |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# UR supports several environment variables, found under UR/ENV |
37
|
|
|
|
|
|
|
# Any UR_* variable which is set but does NOT corresponde to a module found will cause an exit |
38
|
|
|
|
|
|
|
# (a hedge against typos such as UR_DBI_NO_COMMMMIT=1 leading to unexpected behavior) |
39
|
|
|
|
|
|
|
for my $e (keys %ENV) { |
40
|
|
|
|
|
|
|
next unless substr($e,0,3) eq 'UR_'; |
41
|
|
|
|
|
|
|
eval "use UR::Env::$e"; |
42
|
|
|
|
|
|
|
if ($@) { |
43
|
|
|
|
|
|
|
my $path = __FILE__; |
44
|
|
|
|
|
|
|
$path =~ s/.pm$//; |
45
|
|
|
|
|
|
|
my @files = glob($path . '/Env/*'); |
46
|
|
|
|
|
|
|
my @vars = map { /UR\/Env\/(.*).pm/; $1 } @files; |
47
|
|
|
|
|
|
|
print STDERR "Environment variable $e set to $ENV{$e} but there were errors using UR::Env::$e:\n" |
48
|
|
|
|
|
|
|
. "Available variables:\n\t" |
49
|
|
|
|
|
|
|
. join("\n\t",@vars) |
50
|
|
|
|
|
|
|
. "\n"; |
51
|
|
|
|
|
|
|
exit 1; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# These two dump info about used modules and libraries at program exit. |
56
|
|
|
|
|
|
|
END { |
57
|
266
|
50
|
|
266
|
|
11747
|
if ($ENV{UR_USED_LIBS}) { |
58
|
0
|
|
|
|
|
0
|
print STDERR "Used library include paths (\@INC):\n"; |
59
|
0
|
|
|
|
|
0
|
for my $lib (@INC) { |
60
|
0
|
|
|
|
|
0
|
print STDERR "$lib\n"; |
61
|
|
|
|
|
|
|
} |
62
|
0
|
|
|
|
|
0
|
print STDERR "\n"; |
63
|
|
|
|
|
|
|
} |
64
|
266
|
50
|
|
|
|
1043
|
if ($ENV{UR_USED_MODS}) { |
65
|
0
|
|
|
|
|
0
|
print STDERR "Used modules and paths (\%INC):\n"; |
66
|
0
|
|
|
|
|
0
|
for my $mod (sort keys %INC) { |
67
|
0
|
0
|
|
|
|
0
|
if ($ENV{UR_USED_MODS} > 1) { |
68
|
0
|
|
|
|
|
0
|
print STDERR "$mod => $INC{$mod}\n"; |
69
|
|
|
|
|
|
|
} else { |
70
|
0
|
|
|
|
|
0
|
print STDERR "$mod\n"; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
0
|
|
|
|
|
0
|
print STDERR "\n"; |
74
|
|
|
|
|
|
|
} |
75
|
266
|
50
|
|
|
|
1012
|
if ($ENV{UR_DBI_SUMMARIZE_SQL}) { |
76
|
0
|
|
|
|
|
|
UR::DBI::print_sql_summary(); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
#Class::AutoloadCAN must be used before Class::Autouse, or the can methods will break in confusing ways. |
81
|
266
|
|
|
266
|
|
112741
|
use Class::AutoloadCAN; |
|
266
|
|
|
|
|
170445
|
|
|
266
|
|
|
|
|
1248
|
|
82
|
266
|
|
|
266
|
|
135145
|
use Class::Autouse; |
|
266
|
|
|
|
|
1225705
|
|
|
266
|
|
|
|
|
3854
|
|
83
|
|
|
|
|
|
|
BEGIN { |
84
|
266
|
|
|
266
|
|
20618
|
my $v = $Class::Autouse::VERSION; |
85
|
266
|
0
|
33
|
|
|
11228
|
unless (($v =~ /^\d+\.?\d*$/ && $v >= 2.0) |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
86
|
|
|
|
|
|
|
or $v eq '1.99_02' |
87
|
|
|
|
|
|
|
or $v eq '1.99_04') { |
88
|
0
|
|
|
|
|
0
|
die "UR requires Class::Autouse 2.0 or greater (or 1.99_02 or 1.99_04)!!"; |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
}; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Regular deps |
93
|
266
|
|
|
266
|
|
115187
|
use Date::Format; |
|
266
|
|
|
|
|
1463131
|
|
|
266
|
|
|
|
|
303684
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# |
96
|
|
|
|
|
|
|
# Because UR modules execute code when compiling to define their classes, |
97
|
|
|
|
|
|
|
# and require each other for that code to execute, there are bootstrapping |
98
|
|
|
|
|
|
|
# problems. |
99
|
|
|
|
|
|
|
# |
100
|
|
|
|
|
|
|
# Everything which is part of the core framework "requires" UR |
101
|
|
|
|
|
|
|
# which, of course, executes AFTER it has compiled its SUBS, |
102
|
|
|
|
|
|
|
# but BEFORE it defines its class. |
103
|
|
|
|
|
|
|
# |
104
|
|
|
|
|
|
|
# Everything which _uses_ the core of the framework "uses" its namespace, |
105
|
|
|
|
|
|
|
# either the specific top-level namespace module, or "UR" itself for components/extensions. |
106
|
|
|
|
|
|
|
# |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
require UR::Exit; |
109
|
|
|
|
|
|
|
require UR::Util; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
require UR::DBI::Report; # this is used by UR::DBI |
112
|
|
|
|
|
|
|
require UR::DBI; # this needs a new name, and need only be used by UR::DataSource::RDBMS |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
require UR::ModuleBase; # this should be switched to a role |
115
|
|
|
|
|
|
|
require UR::ModuleConfig; # used by ::Time, and also ::Lock ::Daemon |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
require UR::Object::Iterator; |
118
|
|
|
|
|
|
|
require UR::Context::AutoUnloadPool; |
119
|
|
|
|
|
|
|
require UR::DeletedRef; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
require UR::Object; |
122
|
|
|
|
|
|
|
require UR::Object::Type; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
require UR::Object::Ghost; |
125
|
|
|
|
|
|
|
require UR::Object::Property; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
require UR::Observer; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
require UR::BoolExpr::Util; |
130
|
|
|
|
|
|
|
require UR::BoolExpr; # has meta |
131
|
|
|
|
|
|
|
require UR::BoolExpr::Template; # has meta |
132
|
|
|
|
|
|
|
require UR::BoolExpr::Template::PropertyComparison; # has meta |
133
|
|
|
|
|
|
|
require UR::BoolExpr::Template::Composite; # has meta |
134
|
|
|
|
|
|
|
require UR::BoolExpr::Template::And; # has meta |
135
|
|
|
|
|
|
|
require UR::BoolExpr::Template::Or; # has meta |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
require UR::Object::Index; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# |
140
|
|
|
|
|
|
|
# Define core metadata. |
141
|
|
|
|
|
|
|
# |
142
|
|
|
|
|
|
|
# This is done outside of the actual modules since the define() method |
143
|
|
|
|
|
|
|
# uses all of the modules themselves to do its work. |
144
|
|
|
|
|
|
|
# |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
UR::Object::Type->define( |
147
|
|
|
|
|
|
|
class_name => 'UR::Object', |
148
|
|
|
|
|
|
|
is => [], # the default is to inherit from UR::Object, which is circular, so we explicitly say nothing |
149
|
|
|
|
|
|
|
is_abstract => 1, |
150
|
|
|
|
|
|
|
composite_id_separator => "\t", |
151
|
|
|
|
|
|
|
id_by => [ |
152
|
|
|
|
|
|
|
id => { is => 'Scalar', doc => 'unique identifier' } |
153
|
|
|
|
|
|
|
], |
154
|
|
|
|
|
|
|
id_generator => '-urinternal', |
155
|
|
|
|
|
|
|
); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
UR::Object::Type->define( |
158
|
|
|
|
|
|
|
class_name => "UR::Object::Index", |
159
|
|
|
|
|
|
|
id_by => ['indexed_class_name','indexed_property_string'], |
160
|
|
|
|
|
|
|
has => ['indexed_class_name','indexed_property_string'], |
161
|
|
|
|
|
|
|
is_transactional => 0, |
162
|
|
|
|
|
|
|
); |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
UR::Object::Type->define( |
165
|
|
|
|
|
|
|
class_name => 'UR::Object::Ghost', |
166
|
|
|
|
|
|
|
is_abstract => 1, |
167
|
|
|
|
|
|
|
); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
UR::Object::Type->define( |
170
|
|
|
|
|
|
|
class_name => 'UR::Entity', |
171
|
|
|
|
|
|
|
extends => ['UR::Object'], |
172
|
|
|
|
|
|
|
is_abstract => 1, |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
UR::Object::Type->define( |
176
|
|
|
|
|
|
|
class_name => 'UR::Entity::Ghost', |
177
|
|
|
|
|
|
|
extends => ['UR::Object::Ghost'], |
178
|
|
|
|
|
|
|
is_abstract => 1, |
179
|
|
|
|
|
|
|
); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# MORE METADATA CLASSES |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# For bootstrapping reasons, the properties with default values also need to be listed in |
184
|
|
|
|
|
|
|
# %class_property_defaults defined in UR::Object::Type::Initializer. If you make changes |
185
|
|
|
|
|
|
|
# to default values, please keep these in sync. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
UR::Object::Type->define( |
188
|
|
|
|
|
|
|
class_name => 'UR::Object::Type', |
189
|
|
|
|
|
|
|
doc => 'class/type meta-objects for UR', |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
id_by => 'class_name', |
192
|
|
|
|
|
|
|
sub_classification_method_name => '_resolve_meta_class_name', |
193
|
|
|
|
|
|
|
is_abstract => 1, |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
has => [ |
196
|
|
|
|
|
|
|
class_name => { is => 'Text', len => 256, is_optional => 1, |
197
|
|
|
|
|
|
|
doc => 'the name for the class described' }, |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
properties => { |
200
|
|
|
|
|
|
|
is_many => 1, |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# this is calculated instead of a regular relationship |
203
|
|
|
|
|
|
|
# so we can do appropriate inheritance filtering. |
204
|
|
|
|
|
|
|
# We need an isa operator and its converse |
205
|
|
|
|
|
|
|
# in order to be fully declarative internally here |
206
|
|
|
|
|
|
|
calculate => 'shift->_properties(@_);', |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
doc => 'property meta-objects for the class' |
209
|
|
|
|
|
|
|
}, |
210
|
|
|
|
|
|
|
id_properties => { is_many => 1, |
211
|
|
|
|
|
|
|
calculate => q( grep { defined $_->is_id } shift->_properties(@_) ), |
212
|
|
|
|
|
|
|
doc => 'meta-objects for the ID properties of the class' }, |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
doc => { is => 'Text', len => 1024, is_optional => 1, |
215
|
|
|
|
|
|
|
doc => 'a one-line description of the class/type' }, |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
is_abstract => { is => 'Boolean', default_value => 0, |
218
|
|
|
|
|
|
|
doc => 'abstract classes must be subclassified into a concreate class at create/load time' }, |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
is_final => { is => 'Boolean', default_value => 0, |
221
|
|
|
|
|
|
|
doc => 'further subclassification is prohibited on final classes' }, |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
is_transactional => { is => 'Boolean', default_value => 1, is_optional => 1, |
224
|
|
|
|
|
|
|
doc => 'non-transactional objects are left out of in-memory transactions' }, |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
is_singleton => { is => 'Boolean', default_value => 0, |
227
|
|
|
|
|
|
|
doc => 'singleton classes have only one instance, or have each instance fall into a distinct subclass' }, |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
namespace => { is => 'Text', len => 256, is_optional => 1, |
230
|
|
|
|
|
|
|
doc => 'the first "word" in the class name, which points to a UR::Namespace' }, |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
schema_name => { is => 'Text', len => 256, is_optional => 1, |
233
|
|
|
|
|
|
|
doc => 'an arbitrary grouping for classes for which instances share a common storage system' }, |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
data_source_id => { is => 'Text', len => 256, is_optional => 1, |
236
|
|
|
|
|
|
|
doc => 'for classes which persist beyond their current process, the identifier for their storage manager' }, |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
#data_source_meta => { is => 'UR::DataSource', id_by => 'data_source_id', is_optional => 1, }, |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
generated => { is => 'Boolean', is_transient => 1, default_value => 0, |
241
|
|
|
|
|
|
|
doc => 'an internal flag set when the class meta has fabricated accessors and methods in the class namespace' }, |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
meta_class_name => { is => 'Text', |
244
|
|
|
|
|
|
|
doc => 'even meta-classess have a meta-class' }, |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
composite_id_separator => { is => 'Text', len => 2 , default_value => "\t", is_optional => 1, |
247
|
|
|
|
|
|
|
doc => 'for classes whose objects have a multi-value "id", this overrides using a "\t" to compose/decompose' }, |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
valid_signals => { is => 'ARRAY', is_optional => 1, |
250
|
|
|
|
|
|
|
doc => 'List of non-standard signal names observers can bind to ' }, |
251
|
|
|
|
|
|
|
# details used by the managment of the "real" entity outside of the app (persistence) |
252
|
|
|
|
|
|
|
table_name => { is => 'Text', len => undef, is_optional => 1, |
253
|
|
|
|
|
|
|
doc => 'for classes with a data source, this specifies the table or equivalent data structure which holds instances' }, |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
select_hint => { is => 'Text', len => 1024 , is_optional => 1, |
256
|
|
|
|
|
|
|
doc => 'used to optimize access to underlying storage (database specific)' }, |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
join_hint => { is => 'Text', len => 1024 , is_optional => 1, |
259
|
|
|
|
|
|
|
doc => 'used to optimize access to underlying storage when this class is part of a join (database specific)' }, |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
id_generator => { is => 'Text', len => 256, is_optional => 1, |
262
|
|
|
|
|
|
|
doc => 'override the default choice for generating new object IDs' }, |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# different ways of handling subclassing at object load time |
265
|
|
|
|
|
|
|
subclassify_by => { is => 'Text', len => 256, is_optional => 1, |
266
|
|
|
|
|
|
|
doc => 'when set, the method specified will return the name of a specific subclass into which the object should go' }, |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
subclass_description_preprocessor => { is => 'MethodName', len => 255, is_optional => 1, |
269
|
|
|
|
|
|
|
doc => 'a method which should pre-process the class description of sub-classes before construction' }, |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub_classification_method_name => { is => 'Text', len => 256, is_optional => 1, |
272
|
|
|
|
|
|
|
doc => 'like subclassify_by, but examines whole objects not a single property' }, |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
use_parallel_versions => { is => 'Boolean', is_optional => 1, default_value => 0, |
275
|
|
|
|
|
|
|
doc => 'inheriting from the is class will redirect to a ::V? module implemeting a specific version' }, |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# obsolete/internal |
278
|
|
|
|
|
|
|
type_name => { is => 'Text', len => 256, is_deprecated => 1, is_optional => 1 }, |
279
|
|
|
|
|
|
|
er_role => { is => 'Text', len => 256, is_optional => 1, default_value => 'entity' }, |
280
|
|
|
|
|
|
|
source => { is => 'Text', len => 256 , default_value => 'data dictionary', is_optional => 1 }, # This is obsolete and should be removed later |
281
|
|
|
|
|
|
|
sub_classification_meta_class_name => { is => 'Text', len => 1024 , is_optional => 1, |
282
|
|
|
|
|
|
|
doc => 'obsolete' }, |
283
|
|
|
|
|
|
|
first_sub_classification_method_name => { is => 'Text', len => 256, is_optional => 1, |
284
|
|
|
|
|
|
|
doc => 'cached value to handle a complex inheritance hierarchy with storage at some levels but not others' }, |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
### Relationships with the other meta-classes (used internally) ### |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# UR::Namespaces are singletons referenced through their name |
290
|
|
|
|
|
|
|
namespace_meta => { is => 'UR::Namespace', id_by => 'namespace' }, |
291
|
|
|
|
|
|
|
is => { is => 'ARRAY', is_mutable => 0, doc => 'List of the parent class names' }, |
292
|
|
|
|
|
|
|
roles => { is => 'ARRAY', is_mutable => 0, is_optional => 1, doc => 'List of the roles consumed by this class' }, |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# linking to the direct parents, and the complete ancestry |
295
|
|
|
|
|
|
|
parent_class_metas => { is => 'UR::Object::Type', id_by => 'is', |
296
|
|
|
|
|
|
|
doc => 'The list of UR::Object::Type objects for the classes that are direct parents of this class' },#, is_many => 1 }, |
297
|
|
|
|
|
|
|
parent_class_names => { via => 'parent_class_metas', to => 'class_name', is_many => 1 }, |
298
|
|
|
|
|
|
|
parent_meta_class_names => { via => 'parent_class_metas', to => 'meta_class_name', is_many => 1 }, |
299
|
|
|
|
|
|
|
ancestry_meta_class_names => { via => 'ancestry_class_metas', to => 'meta_class_name', is_many => 1 }, |
300
|
|
|
|
|
|
|
ancestry_class_metas => { is => 'UR::Object::Type', id_by => 'is', where => [-recurse => [class_name => 'is']], |
301
|
|
|
|
|
|
|
doc => 'Climb the ancestry tree and return the class objects for all of them' }, |
302
|
|
|
|
|
|
|
ancestry_class_names => { via => 'ancestry_class_metas', to => 'class_name', is_many => 1 }, |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# This one isn't useful on its own, but is used to build the all_* accessors below |
305
|
|
|
|
|
|
|
all_class_metas => { is => 'UR::Object::Type', calculate => 'return ($self, $self->ancestry_class_metas)' }, |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Properties defined on this class, parent classes, etc. |
308
|
|
|
|
|
|
|
# There's also a property_meta_by_name() method defined in the class |
309
|
|
|
|
|
|
|
direct_property_metas => { is => 'UR::Object::Property', reverse_as => 'class_meta', is_many => 1 }, |
310
|
|
|
|
|
|
|
direct_property_names => { via => 'direct_property_metas', to => 'property_name', is_many => 1 }, |
311
|
|
|
|
|
|
|
direct_id_property_metas => { is => 'UR::Object::Property', reverse_as => 'class_meta', where => [ 'is_id true' => 1, -order_by => 'is_id' ], is_many => 1 }, |
312
|
|
|
|
|
|
|
direct_id_property_names => { via => 'direct_id_property_metas', to => 'property_name', is_many => 1 }, |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
ancestry_property_metas => { via => 'ancestry_class_metas', to => 'direct_property_metas', is_many => 1 }, |
315
|
|
|
|
|
|
|
ancestry_property_names => { via => 'ancestry_class_metas', to => 'direct_property_names', is_many => 1 }, |
316
|
|
|
|
|
|
|
ancestry_id_property_metas => { via => 'ancestry_class_metas', to => 'direct_id_property_metas', is_many => 1 }, |
317
|
|
|
|
|
|
|
ancestry_id_property_names => { via => 'ancestry_id_property_metas', to => 'property_name', is_many => 1 }, |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
all_property_metas => { via => 'all_class_metas', to => 'direct_property_metas', is_many => 1 }, |
320
|
|
|
|
|
|
|
all_property_names => { via => 'all_property_metas', to => 'property_name', is_many => 1 }, |
321
|
|
|
|
|
|
|
all_id_property_metas => { via => 'all_class_metas', to => 'direct_id_property_metas', is_many => 1 }, |
322
|
|
|
|
|
|
|
all_id_property_names => { via => 'all_id_property_metas', to => 'property_name', is_many => 1 }, |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
direct_id_by_property_metas => { via => 'direct_property_metas', to => '__self__', where => ['id_by true' => 1], is_many => 1, doc => "Properties with 'id_by' metadata, ie. direct object accessor properties" } , |
325
|
|
|
|
|
|
|
all_id_by_property_metas => { via => 'all_class_metas', to => 'direct_id_by_property_metas', is_many => 1}, |
326
|
|
|
|
|
|
|
direct_reverse_as_property_metas => { via => 'direct_property_metas', to => '__self__', where => ['reverse_as true' => 1], is_many => 1, doc => "Properties with 'reverse_as' metadata, ie. indirect object accessor properties" }, |
327
|
|
|
|
|
|
|
all_reverse_as_property_metas => { via => 'all_class_metas', to => 'direct_reverse_as_property_metas', is_many => 1}, |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# Datasource related stuff |
330
|
|
|
|
|
|
|
direct_column_names => { via => 'direct_property_metas', to => 'column_name', is_many => 1, where => [column_name => { operator => 'true' }] }, |
331
|
|
|
|
|
|
|
direct_id_column_names => { via => 'direct_id_property_metas', to => 'column_name', is_many => 1, where => [column_name => { operator => 'true'}] }, |
332
|
|
|
|
|
|
|
ancestry_column_names => { via => 'ancestry_class_metas', to => 'direct_column_names', is_many => 1 }, |
333
|
|
|
|
|
|
|
ancestry_id_column_names => { via => 'ancestry_class_metas', to => 'direct_id_column_names', is_many => 1 }, |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Are these *columnless* properties actually necessary? The user could just use direct_property_metas(column_name => undef) |
336
|
|
|
|
|
|
|
direct_columnless_property_metas => { is => 'UR::Object::Property', reverse_as => 'class_meta', where => [column_name => undef], is_many => 1 }, |
337
|
|
|
|
|
|
|
direct_columnless_property_names => { via => 'direct_columnless_property_metas', to => 'property_name', is_many => 1 }, |
338
|
|
|
|
|
|
|
ancestry_columnless_property_metas => { via => 'ancestry_class_metas', to => 'direct_columnless_property_metas', is_many => 1 }, |
339
|
|
|
|
|
|
|
ancestry_columnless_property_names => { via => 'ancestry_columnless_property_metas', to => 'property_name', is_many => 1 }, |
340
|
|
|
|
|
|
|
ancestry_table_names => { via => 'ancestry_class_metas', to => 'table_name', is_many => 1 }, |
341
|
|
|
|
|
|
|
all_table_names => { via => 'all_class_metas', to => 'table_name', is_many => 1 }, |
342
|
|
|
|
|
|
|
all_column_names => { via => 'all_class_metas', to => 'direct_column_names', is_many => 1 }, |
343
|
|
|
|
|
|
|
all_id_column_names => { via => 'all_class_metas', to => 'direct_id_column_names', is_many => 1 }, |
344
|
|
|
|
|
|
|
all_columnless_property_metas => { via => 'all_class_metas', to => 'direct_columnless_property_metas', is_many => 1 }, |
345
|
|
|
|
|
|
|
all_columnless_property_names => { via => 'all_class_metas', to => 'direct_columnless_property_names', is_many => 1 }, |
346
|
|
|
|
|
|
|
], |
347
|
|
|
|
|
|
|
); |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
UR::Object::Type->define( |
350
|
|
|
|
|
|
|
class_name => 'UR::Object::Property', |
351
|
|
|
|
|
|
|
id_properties => [ |
352
|
|
|
|
|
|
|
class_name => { is => 'Text', len => 256 }, |
353
|
|
|
|
|
|
|
property_name => { is => 'Text', len => 256 }, |
354
|
|
|
|
|
|
|
], |
355
|
|
|
|
|
|
|
has_optional => [ |
356
|
|
|
|
|
|
|
property_type => { is => 'Text', len => 256 , is_optional => 1}, |
357
|
|
|
|
|
|
|
column_name => { is => 'Text', len => 256, is_optional => 1 }, |
358
|
|
|
|
|
|
|
data_length => { is => 'Text', len => 32, is_optional => 1 }, |
359
|
|
|
|
|
|
|
data_type => { is => 'Text', len => 256, is_optional => 1 }, |
360
|
|
|
|
|
|
|
calculated_default => { is_optional => 1 }, |
361
|
|
|
|
|
|
|
default_value => { is_optional => 1 }, |
362
|
|
|
|
|
|
|
valid_values => { is => 'ARRAY', is_optional => 1, }, |
363
|
|
|
|
|
|
|
example_values => { is => 'ARRAY', is_optional => 1, doc => 'example valid values; used to generate help text for Commands' }, |
364
|
|
|
|
|
|
|
doc => { is => 'Text', len => 1000, is_optional => 1 }, |
365
|
|
|
|
|
|
|
is_id => { is => 'Integer', default_value => undef, doc => 'denotes this is an ID property of the class, and ranks them' }, |
366
|
|
|
|
|
|
|
is_optional => { is => 'Boolean' , default_value => 0}, |
367
|
|
|
|
|
|
|
is_transient => { is => 'Boolean' , default_value => 0}, |
368
|
|
|
|
|
|
|
is_constant => { is => 'Boolean' , default_value => 0}, # never changes |
369
|
|
|
|
|
|
|
is_mutable => { is => 'Boolean' , default_value => 1}, # can be changed explicitly via accessor (cannot be constant) |
370
|
|
|
|
|
|
|
is_volatile => { is => 'Boolean' , default_value => 0}, # changes w/o a signal: (cannot be constant or transactional) |
371
|
|
|
|
|
|
|
is_classwide => { is => 'Boolean' , default_value => 0}, |
372
|
|
|
|
|
|
|
is_delegated => { is => 'Boolean' , default_value => 0}, |
373
|
|
|
|
|
|
|
is_calculated => { is => 'Boolean' , default_value => 0}, |
374
|
|
|
|
|
|
|
is_transactional => { is => 'Boolean' , default_value => 1}, # STM works on these, and the object can possibly save outside the app |
375
|
|
|
|
|
|
|
is_abstract => { is => 'Boolean' , default_value => 0}, |
376
|
|
|
|
|
|
|
is_concrete => { is => 'Boolean' , default_value => 1}, |
377
|
|
|
|
|
|
|
is_final => { is => 'Boolean' , default_value => 0}, |
378
|
|
|
|
|
|
|
is_many => { is => 'Boolean' , default_value => 0}, |
379
|
|
|
|
|
|
|
is_aggregate => { is => 'Boolean' , default_value => 0}, |
380
|
|
|
|
|
|
|
is_deprecated => { is => 'Boolean', default_value => 0}, |
381
|
|
|
|
|
|
|
is_numeric => { calculate_from => ['data_type'], }, |
382
|
|
|
|
|
|
|
id_by => { is => 'ARRAY', is_optional => 1}, |
383
|
|
|
|
|
|
|
id_class_by => { is => 'Text', is_optional => 1}, |
384
|
|
|
|
|
|
|
is_undocumented => { is => 'Boolean', is_optional => 1, doc => 'do not show in documentation to users' }, |
385
|
|
|
|
|
|
|
doc_position => { is => 'Number', is_optional => 1, doc => 'override the sort position within documentation' }, |
386
|
|
|
|
|
|
|
access_as => { is => 'Text', is_optional => 1, doc => 'when id_class_by is set, and this is set to "auto", primitives will return as their ID instead of boxed' }, |
387
|
|
|
|
|
|
|
order_by => { is => 'ARRAY', is_optional => 1}, |
388
|
|
|
|
|
|
|
specify_by => { is => 'Text', is_optional => 1}, |
389
|
|
|
|
|
|
|
reverse_as => { is => 'ARRAY', is_optional => 1 }, |
390
|
|
|
|
|
|
|
implied_by => { is => 'Text' , is_optional => 1}, |
391
|
|
|
|
|
|
|
via => { is => 'Text' , is_optional => 1 }, |
392
|
|
|
|
|
|
|
to => { is => 'Text' , is_optional => 1}, |
393
|
|
|
|
|
|
|
where => { is => 'ARRAY', is_optional => 1}, |
394
|
|
|
|
|
|
|
calculate => { is => 'Text' , is_optional => 1}, |
395
|
|
|
|
|
|
|
calculate_from => { is => 'ARRAY' , is_optional => 1}, |
396
|
|
|
|
|
|
|
calculate_perl => { is => 'Perl' , is_optional => 1}, |
397
|
|
|
|
|
|
|
calculate_sql => { is => 'SQL' , is_optional => 1}, |
398
|
|
|
|
|
|
|
calculate_js => { is => 'JavaScript' , is_optional => 1}, |
399
|
|
|
|
|
|
|
constraint_name => { is => 'Text' , is_optional => 1}, |
400
|
|
|
|
|
|
|
is_legacy_eav => { is => 'Boolean' , is_optional => 1}, |
401
|
|
|
|
|
|
|
is_dimension => { is => 'Boolean', is_optional => 1}, |
402
|
|
|
|
|
|
|
is_specified_in_module_header => { is => 'Boolean', default_value => 0 }, |
403
|
|
|
|
|
|
|
position_in_module_header => { is => 'Integer', is_optional => 1, doc => "Line in the class definition source's section this property appears" }, |
404
|
|
|
|
|
|
|
singular_name => { is => 'Text' }, |
405
|
|
|
|
|
|
|
plural_name => { is => 'Text' }, |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
class_meta => { is => 'UR::Object::Type', id_by => 'class_name' }, |
408
|
|
|
|
|
|
|
r_class_meta => { is => 'UR::Object::Type', id_by => 'data_type' }, |
409
|
|
|
|
|
|
|
], |
410
|
|
|
|
|
|
|
unique_constraints => [ |
411
|
|
|
|
|
|
|
{ properties => [qw/property_name class_name/], sql => 'SUPER_FAKE_O4' }, |
412
|
|
|
|
|
|
|
], |
413
|
|
|
|
|
|
|
); |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
UR::Object::Type->define( |
417
|
|
|
|
|
|
|
class_name => 'UR::Object::Property::Calculated::From', |
418
|
|
|
|
|
|
|
id_properties => [qw/class_name calculated_property_name source_property_name/], |
419
|
|
|
|
|
|
|
); |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
require UR::Singleton; |
422
|
|
|
|
|
|
|
require UR::Namespace; |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
UR::Object::Type->define( |
425
|
|
|
|
|
|
|
class_name => 'UR', |
426
|
|
|
|
|
|
|
extends => ['UR::Namespace'], |
427
|
|
|
|
|
|
|
); |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
require UR::Context; |
430
|
|
|
|
|
|
|
UR::Object::Type->initialize_bootstrap_classes; |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
require UR::Role; |
433
|
|
|
|
|
|
|
require Command; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
$UR::initialized = 1; |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
require UR::Change; |
438
|
|
|
|
|
|
|
require UR::Context::Root; |
439
|
|
|
|
|
|
|
require UR::Context::Process; |
440
|
|
|
|
|
|
|
require UR::Object::Tag; |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
do { |
443
|
|
|
|
|
|
|
UR::Context->_initialize_for_current_process(); |
444
|
|
|
|
|
|
|
}; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
require UR::ModuleLoader; # signs us up with Class::Autouse |
447
|
|
|
|
|
|
|
require UR::Value::Iterator; |
448
|
|
|
|
|
|
|
require UR::Object::View; |
449
|
|
|
|
|
|
|
require UR::Object::Join; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub main::ur_core { |
452
|
0
|
|
|
0
|
|
|
print STDERR "Dumping rules and templates to ./ur_core.stor...\n"; |
453
|
0
|
|
|
|
|
|
my $dump; |
454
|
0
|
0
|
|
|
|
|
unless(open($dump, ">ur_core.stor")) { |
455
|
0
|
|
|
|
|
|
print STDERR "Can't open ur_core.stor for writing: $!"; |
456
|
0
|
|
|
|
|
|
exit; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
store_fd([ |
459
|
0
|
|
|
|
|
|
$UR::Object::rule_templates, |
460
|
|
|
|
|
|
|
$UR::Object::rules, |
461
|
|
|
|
|
|
|
], |
462
|
|
|
|
|
|
|
$dump); |
463
|
0
|
|
|
|
|
|
close $dump; |
464
|
0
|
|
|
|
|
|
exit(); |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
1; |
468
|
|
|
|
|
|
|
__END__ |