File Coverage

lib/UR/Object/Type/InternalAPI.pm
Criterion Covered Total %
statement 839 983 85.3
branch 282 392 71.9
condition 86 137 62.7
subroutine 159 159 100.0
pod 22 55 40.0
total 1388 1726 80.4


line stmt bran cond sub pod time code
1             package UR::Object::Type;
2 461     461   77236 use warnings;
  439         845  
  439         8745  
3 271     271   2413 use strict;
  268         270  
  268         7847  
4              
5             require UR;
6             our $VERSION = "0.46"; # UR $VERSION;
7              
8 268     268   1432 use Sys::Hostname;
  267         290  
  267         9496  
9 268     268   1727 use Cwd;
  267         294  
  267         12983  
10 270     270   1462 use Scalar::Util qw(blessed);
  269         299  
  269         8802  
11 270     270   1571 use Sub::Name;
  269         339  
  269         624503  
12              
13             our %meta_classes;
14             our $bootstrapping = 1;
15             our @partially_defined_classes;
16             our $pwd_at_compile_time = cwd();
17              
18             # each method which caches data on the class for properties stores its hash key here
19             # when properties mutate this is cleared
20             our @cache_keys;
21              
22             sub property_metas {
23 205     205 0 77665 my $self = $_[0];
24 174         567 my @a = map { $self->property_meta_for_name($_) } $self->all_property_names();
  220         1679  
25 196         71668 return @a;
26             }
27              
28             # Some accessor methods drawn from properties need to be overridden.
29             # Some times because they need to operate during bootstrapping. Sometimes
30             # because the method needs some special behavior like sorting or filtering.
31             # Sometimes to optimize performance or cache data
32              
33             # This needs to remain overridden to enforce the restriction on callers
34             sub data_source {
35 299340     299374 0 243022 my $self = shift;
36 299340         585681 my $ds = $self->data_source_id(@_);
37            
38 299361 100       686387 return undef unless $ds;
39 10821         12123 local $@;
40 10821 100       15522 my $obj = eval { UR::DataSource->get($ds) || $ds->get() };
  10853         92683  
41              
42 10824         24336 return $obj;
43             }
44              
45             sub ancestry_class_metas {
46             #my $rule_template = UR::BoolExpr::Template->resolve(__PACKAGE__,'id');
47              
48             # Can't use the speed optimization of getting a template here. Using the Context to get
49             # objects here causes endless recursion during bootstrapping
50 36968     37003 1 86025 map { __PACKAGE__->get($_) } shift->ancestry_class_names;
  72647         219583  
51             #return map { $UR::Context::current->get_objects_for_class_and_rule(__PACKAGE__, $_) }
52             # map { $rule_template->get_rule_for_values($_) }
53             # shift->ancestry_class_names;
54              
55             }
56              
57             our $PROPERTY_META_FOR_NAME_TEMPLATE;
58             push @cache_keys, '_property_meta_for_name';
59             sub property_meta_for_name {
60 271907     271940 1 259134 my ($self, $property_name) = @_;
61              
62 271907 50       358920 return unless $property_name;
63              
64 271922 100       494689 if (index($property_name,'.') != -1) {
65 185         618 my @chain = split(/\./,$property_name);
66 185         857 my $last_class_meta = $self;
67 239         53952 my $last_class_name = $self->id;
68 200         1104 my @pmeta;
69 200         1092 for my $full_link (@chain) {
70 426         1842 my ($link) = ($full_link =~ /^([^\-\?]+)/);
71 426         943 my $property_meta = $last_class_meta->property_meta_for_name($link);
72 426         1039 push @pmeta, $property_meta;
73 357 100       48528 last if $link eq $chain[-1];
74 221         900 my @joins = UR::Object::Join->resolve_chain($last_class_name, $link);
75 221 100       1168 return unless @joins;
76              
77 301         1290 $last_class_name = $joins[-1]{foreign_class};
78 301         699 $last_class_meta = $last_class_name->__meta__;
79             }
80 279 100 33     1189 return unless (@pmeta and $pmeta[-1]);
81 198 100       35275 return @pmeta if wantarray;
82 67         367 return $pmeta[-1];
83             }
84              
85 271723         214938 my $pos = index($property_name,'-');
86 271835 100       339348 if ($pos != -1) {
87 174         266 $property_name = substr($property_name,0,$pos);
88             }
89              
90 271835 100 66     730056 if (exists($self->{'_property_meta_for_name'}) and $self->{'_property_meta_for_name'}->{$property_name}) {
91 201113         373376 return $self->{'_property_meta_for_name'}->{$property_name};
92             }
93 70695   66     161389 $PROPERTY_META_FOR_NAME_TEMPLATE ||= UR::BoolExpr::Template->resolve('UR::Object::Property', 'class_name', 'property_name');
94              
95 70695         68997 my $property;
96 70790         144724 for my $class ($self->class_name, $self->ancestry_class_names) {
97 192988         424974 my $rule = $PROPERTY_META_FOR_NAME_TEMPLATE->get_rule_for_values($class, $property_name);
98 192988         465884 $property = $UR::Context::current->get_objects_for_class_and_rule('UR::Object::Property', $rule);
99 192904 100       441419 if ($property) {
100 43193         138799 return $self->{'_property_meta_for_name'}->{$property_name} = $property;
101             }
102             }
103 27517         49555 return;
104             }
105              
106             # A front-end for property_meta_for_name, but
107             # will translate the generic 'id' property into the class' real ID property,
108             # if it's not called 'id'
109             sub _concrete_property_meta_for_class_and_name {
110 4680     4762   29020 my($self,$property_name) = @_;
111              
112 4653         8984 my @property_metas = $self->property_meta_for_name($property_name);
113              
114 4653         12984 for (my $i = 0; $i < @property_metas; $i++) {
115 2943 100 100     8469 if ($property_metas[$i]->id eq "UR::Object\tid"
116             and $property_name !~ /\./) #If we're looking at a foreign object's id, can't replace with our own
117             {
118             # This is the generic id property. Remap it to the class' real ID property name
119 217         473 my @id_properties = $self->id_property_names;
120 217 100 100     1320 if (@id_properties == 1 and $id_properties[0] eq 'id') {
121 170         3470 next; # this class doesn't have any other ID properties
122             }
123             #return map { $self->_concrete_property_meta_for_class_and_name($_) } @id_properties;
124 165         677 my @remapped = map { $self->_concrete_property_meta_for_class_and_name($_) } @id_properties;
  186         886  
125 87         21247 splice(@property_metas, $i, 1, @remapped);
126             }
127             }
128 4655         9582 return @property_metas;
129             }
130              
131              
132              
133             sub _flatten_property_name {
134 75     171   416 my ($self, $name) = @_;
135            
136 82         14917 my $flattened_name = '';
137 66         472 my @add_keys;
138             my @add_values;
139              
140 66         382 my @meta = $self->property_meta_for_name($name);
141 151         682 for my $meta (@meta) {
142 174         299 my @joins = $meta->_resolve_join_chain();
143 174         642 for my $join (@joins) {
144 168 100       786 if ($flattened_name) {
145 128         187 $flattened_name .= '.';
146             }
147 168         552 $flattened_name .= $join->{source_name_for_foreign};
148 155 100       3115 if (my $where = $join->{where}) {
149 96         494 $flattened_name .= '-' . $join->sub_group_label;
150 96         519 my $join_class = $join->{foreign_class};
151 46         15593 my $bx2 = UR::BoolExpr->resolve($join_class,@$where);
152 35         471 my $bx2_flat = $bx2->flatten(); # recurses through this
153 35         337 my ($bx2_flat_template, @values) = $bx2_flat->template_and_values();
154 41         14991 my @keys = @{ $bx2_flat_template->{_keys} };
  31         336  
155 31         275 for my $key (@keys) {
156 82 50       1008 next if substr($key,0,1) eq '-';
157 82         141 my $full_key = $flattened_name . '?.' . $key;
158 82         426 push @add_keys, $full_key;
159 63         341 push @add_values, shift @values;
160             }
161 63 50       130 if (@values) {
162 55         297 Carp:confess("Unexpected mismatch in count of keys and values!");
163             }
164             }
165             }
166             }
167 84         425 return ($flattened_name, \@add_keys, \@add_values);
168             };
169              
170             our $DIRECT_ID_PROPERTY_METAS_TEMPLATE;
171             sub direct_id_property_metas {
172 32744     32822 1 70737 my $self = _object(shift);
173 32744   66     89385 $DIRECT_ID_PROPERTY_METAS_TEMPLATE ||= UR::BoolExpr::Template->resolve('UR::Object::Property', 'class_name', 'property_name', 'is_id >=');
174 32741         78781 my $class_name = $self->class_name;
175             my @id_property_objects =
176 15539         43455 map { $UR::Context::current->get_objects_for_class_and_rule('UR::Object::Property', $_) }
177 15527         49131 map { $DIRECT_ID_PROPERTY_METAS_TEMPLATE->get_rule_for_values($class_name, $_, 0) }
178 32741         40447 @{$self->{'id_by'}};
  32721         67825  
179              
180 32721     8049   99344 my $sort_sub = sub ($$) { return $_[0]->is_id cmp $_[1]->is_id };
  7901         30272  
181 32720         60556 @id_property_objects = sort $sort_sub @id_property_objects;
182 32720 100       71582 if (@id_property_objects == 0) {
183 23382         72050 @id_property_objects = $self->property_meta_for_name("id");
184             }
185 32737         167680 return @id_property_objects;
186             }
187              
188             sub parent_class_names {
189 63640     63710 1 66976 my $self = shift;
190 63633         52056 return @{ $self->{is} };
  63633         167948  
191             }
192              
193              
194             # If $property_name represents an alias-type property (via => '__self__'),
195             # then return a string with all the aliases removed
196             push @cache_keys, '_resolve_property_aliases';
197             sub resolve_property_aliases {
198 14367     14505 0 16346 my($self,$property_name) = @_;
199              
200 14365 50       33384 return unless $property_name;
201 14354 100 66     43286 unless ($self->{'_resolve_property_aliases'} && $self->{'_resolve_property_aliases'}->{$property_name}) {
202 6560   100     20041 $self->{'_resolve_property_aliases'} ||= {};
203              
204 6567         24587 my @property_metas = $self->property_meta_for_name($property_name);
205 6556         7804 my @property_names;
206 6556 100       11348 if (@property_metas) {
207 6561         9470 @property_names = map { $_->alias_for } @property_metas;
  6620         20097  
208             } else {
209             # there was a problem resolving the chain of properties
210             # This happens in the case of an object accessor (is => 'Some::Class') without an id_by
211 36         168 my @split_names = split(/\./,$property_name);
212 29         127 my $name_count = @split_names;
213 29         55 my $prop_meta = $self->property_meta_for_name(shift @split_names);
214 29 100       141 return unless $prop_meta;
215 23   66     6450 my $foreign_class = $prop_meta->data_type && eval { $prop_meta->data_type->__meta__};
216 12 100       192 return unless $foreign_class;
217 11         109 @property_names = ( $prop_meta->alias_for, $foreign_class->resolve_property_aliases(join('.', @split_names)));
218 19 100       6040 unless (@property_names >= $name_count) {
219 10         84 Carp::croak("Some parts from property '$property_name' of class ".$self->class_name
220             . " didn't resolve");
221             }
222             }
223 6544         18970 $self->{'_resolve_property_aliases'}->{$property_name} = join('.', @property_names);
224             }
225 14346         27798 return $self->{'_resolve_property_aliases'}->{$property_name};
226             }
227              
228              
229             push @cache_keys, '_id_property_names';
230             sub id_property_names {
231             # FIXME Take a look at id_property_names and all_id_property_names.
232             # They look extremely similar, but tests start dying if you replace one
233             # with the other, or remove both and rely on the property's accessor method
234              
235 42746     42818 0 78650 my $self = _object(shift);
236              
237 42746 100       89520 unless ($self->{'_id_property_names'}) {
238 5297         5359 my @id_by;
239 5297 100 66     15102 unless ($self->{id_by} and @id_by = @{ $self->{id_by} }) {
  5297         21723  
240 3133         10590 foreach my $parent ( @{ $self->{'is'} } ) {
  3125         8554  
241 3125         12136 my $parent_class = $parent->class->__meta__;
242 3131 50       12908 next unless $parent_class;
243 3125         11088 @id_by = $parent_class->id_property_names;
244 3125 50       8130 last if @id_by;
245             }
246             }
247 5295         11061 $self->{'_id_property_names'} = \@id_by;
248             }
249 42741         37368 return @{$self->{'_id_property_names'}};
  42741         104928  
250             }
251              
252             push @cache_keys, '_all_id_property_names';
253             sub all_id_property_names {
254             # return shift->id_property_names(@_); This makes URT/t/99_transaction.t fail
255 7842     7989 1 12256 my $self = shift;
256 7832 100       17924 unless ($self->{_all_id_property_names}) {
257 471         1089 my ($tmp,$last) = ('','');
258             $self->{_all_id_property_names} = [
259 1223         1369 grep { $tmp = $last; $last = $_; $tmp ne $_ }
  1223         1186  
  1227         10435  
260             sort
261 1581         2119 map { @{ $_->{id_by} } }
  1581         4399  
262 479         1631 map { __PACKAGE__->get($_) }
  1586         8987  
263             ($self->class_name, $self->ancestry_class_names)
264             ];
265             }
266 7841         7966 return @{ $self->{_all_id_property_names} };
  7841         25741  
267             }
268              
269             sub direct_id_column_names {
270 197     257 1 6403 my $self = _object(shift);
271             my @id_column_names =
272 192         546 map { $_->column_name }
  241         790  
273             $self->direct_id_property_metas;
274 195         6043 return @id_column_names;
275             }
276              
277              
278             sub ancestry_table_names {
279 206     343 1 584 my $self = _object(shift);
280             my @inherited_table_names =
281 501         7377 grep { defined($_) }
282 206         821 map { $_->table_name }
  497         1168  
283             $self->ancestry_class_metas;
284 204         524 return @inherited_table_names;
285             }
286              
287             sub all_table_names {
288 207     259 1 5553 my $self = _object(shift);
289             my @table_names =
290 202         1084 grep { defined($_) }
  230         547  
291             ( $self->table_name, $self->ancestry_table_names );
292 203         1574 return @table_names;
293             }
294              
295             sub first_table_name {
296 14376     14427 0 21293 my $self = _object(shift);
297 14376 50       25929 if ($self->{_first_table_name}) {
298 10         524 return $self->{first_table_name};
299             }
300              
301 14376         16224 my @classes = ($self);
302 14376         22171 while(@classes) {
303 18981         17456 my $co = shift @classes;
304 18979 100       33367 if (my $table_name = $co->table_name) {
305 7438         8679 $self->{first_table_name} = $table_name;
306 7445         16136 return $table_name;
307             }
308 11556         9691 my @parents = map { $_->__meta__ } @{$co->{'is'}};
  4620         17154  
  11557         17911  
309 11554         20438 push @classes, @parents;
310             }
311 6953         10170 return;
312             }
313            
314              
315             sub ancestry_class_names {
316 125285     125415 1 125293 my $self = shift;
317            
318 125278 100       233676 if ($self->{_ordered_inherited_class_names}) {
319 101774         87896 return @{ $self->{_ordered_inherited_class_names} };
  101777         235309  
320             }
321            
322 23511         22583 my $ordered_inherited_class_names = $self->{_ordered_inherited_class_names} = [ @{ $self->{is} } ];
  23511         63016  
323 23516         43206 my @unchecked = @$ordered_inherited_class_names;
324 23508         52789 my %seen = ( $self->{class_name} => 1 );
325 23508         58926 while (my $ancestor_class_name = shift @unchecked) {
326 58171 100       95905 next if $seen{$ancestor_class_name};
327 55085         60841 $seen{$ancestor_class_name} = 1;
328 55085         133731 my $class_meta = $ancestor_class_name->__meta__;
329 55090 50       86845 Carp::confess("Can't find meta for $ancestor_class_name!") unless $class_meta;
330 55086 50       92349 next unless $class_meta->{is};
331 55086         45498 push @$ordered_inherited_class_names, @{ $class_meta->{is} };
  55090         73459  
332 55085         42565 unshift @unchecked, $_ for reverse @{ $class_meta->{is} };
  55085         153131  
333             }
334 23514         58002 return @$ordered_inherited_class_names;
335             }
336              
337             push @cache_keys, '_all_property_names';
338             sub all_property_names {
339 5933     6062 1 6992 my $self = shift;
340            
341 5933 100       15217 if ($self->{_all_property_names}) {
342 3460         3945 return @{ $self->{_all_property_names} };
  3455         16058  
343             }
344            
345 2482         5071 my %seen = ();
346 2489         10112 my $all_property_names = $self->{_all_property_names} = [];
347 2481         9520 for my $class_name ($self->class_name, $self->ancestry_class_names) {
348 10410 100       19249 next if $class_name eq 'UR::Object';
349 7132         19500 my $class_meta = UR::Object::Type->get($class_name);
350 7124 50       15665 if (my $has = $class_meta->{has}) {
351             push @$all_property_names,
352             grep {
353             not exists $has->{$_}{id_by}
354 45689         68059 }
355 7124         41551 grep { !exists $seen{$_} }
  47834         45636  
356             sort keys %$has;
357 7125         11944 foreach (@$all_property_names) {
358 57562         55937 $seen{$_} = 1;
359             }
360             }
361             }
362 2482         15044 return @$all_property_names;
363             }
364              
365              
366             ########################################################################
367             # End of overridden property methods
368             ########################################################################
369              
370             sub _resolve_meta_class_name_for_class_name {
371 24760     24810   26452 my $class = shift;
372 24764         24893 my $class_name = shift;
373             #if ($class_name->isa("UR::Object::Type") or $meta_classes{$class_name} or $class_name =~ '::Type') {
374 24760 100 100     126149 if ($meta_classes{$class_name} or $class_name =~ '::Type') {
375 12862         31814 return "UR::Object::Type"
376             }
377             else {
378 11904         45605 return $class_name . "::Type";
379             }
380             }
381              
382             sub _resolve_meta_class_name {
383 3     42   5 my $class = shift;
384 3         17 my ($rule,%extra) = UR::BoolExpr->resolve_normalized($class, @_);
385 3         873 my %params = $rule->params_list;
386 1         3 my $class_name = $params{class_name};
387 1 0       9 return unless $class_name;
388 2         319 return $class->_resolve_meta_class_name_for_class_name($class_name);
389             }
390              
391              
392             # This method can go away when we have the is_cached meta-property
393             sub first_sub_classification_method_name {
394 1188     1299 0 1876 my $self = shift;
395            
396             # This may be one of many things which class meta-data should "inherit" from classes which
397             # its instances inherit from. This value is set to the value found on the most concrete class
398             # in the inheritance tree.
399              
400 1188 100       3528 return $self->{___first_sub_classification_method_name} if exists $self->{___first_sub_classification_method_name};
401            
402 1163         6577 $self->{___first_sub_classification_method_name} = $self->sub_classification_method_name;
403 1161 100       3212 unless ($self->{___first_sub_classification_method_name}) {
404 937         2647 for my $parent_class ($self->ancestry_class_metas) {
405 1680 100       5150 last if ($self->{___first_sub_classification_method_name} = $parent_class->sub_classification_method_name);
406             }
407             }
408            
409 1161         6347 return $self->{___first_sub_classification_method_name};
410             }
411              
412              
413             # Another thing that is "inherited" from parent class metas
414             sub subclassify_by {
415 403133     403234 0 311370 my $self = shift;
416              
417 403135 100       975903 return $self->{'__subclassify_by'} if exists $self->{'__subclassify_by'};
418              
419 7204         31534 $self->{'__subclassify_by'} = $self->__subclassify_by;
420 7204 100       14984 unless ($self->{'__subclassify_by'}) {
421 7067         15047 for my $parent_class ($self->ancestry_class_metas) {
422 15991 100       30088 last if ($self->{'__subclassify_by'} = $parent_class->__subclassify_by);
423             }
424             }
425              
426 7204         20875 return $self->{'__subclassify_by'};
427             }
428              
429             sub resolve_composite_id_from_ordered_values {
430 300135     300221 0 252000 my $self = shift;
431 300133         457592 my $resolver = $self->get_composite_id_resolver;
432 300133         457339 return $resolver->(@_);
433             }
434              
435             sub resolve_ordered_values_from_composite_id {
436 109     145 0 493 my $self = shift;
437 107         400 my $decomposer = $self->get_composite_id_decomposer;
438 107         281 return $decomposer->(@_);
439             }
440              
441             sub get_composite_id_decomposer {
442 393     424 0 794 my $self = shift;
443 391         491 my $decomposer;
444 391 100       1015 unless ($decomposer = $self->{get_composite_id_decomposer}) {
445 79         904 my @id_property_names = $self->id_property_names;
446 77 100       306 if (@id_property_names == 1) {
447 64     268   374 $decomposer = sub { $_[0] };
  196         889  
448             }
449             else {
450 13         60 my $separator = $self->_resolve_composite_id_separator;
451             $decomposer = sub {
452 247 100   300   695 if (ref($_[0])) {
453             # ID is an arrayref, or we'll throw an exception.
454 5         7 my $id = $_[0];
455 5         7 my $underlying_id_count = scalar(@$id);
456            
457             # Handle each underlying ID, turning each into an arrayref divided by property value.
458 7         303 my @decomposed_ids;
459 5         9 for my $underlying_id (@$id) {
460 13 50       247 push @decomposed_ids, [map { $_ eq '' ? undef : $_ } split($separator,$underlying_id)];
  28         349  
461             }
462            
463             # Count the property values.
464 5 50       13 my $underlying_property_count = scalar(@{$decomposed_ids[0]}) if @decomposed_ids;
  5         9  
465 7   50     307 $underlying_property_count ||= 0;
466            
467             # Make a list of property values, but each value will be an
468             # arrayref of a set of values instead of a single value.
469 5         6 my @property_values;
470 5         16 for (my $n = 0; $n < $underlying_property_count; $n++) {
471 12         317 $property_values[$n] = [ map { $_->[$n] } @decomposed_ids ];
  26         43  
472             }
473 5         20 return @property_values;
474             }
475             else {
476             # Regular scalar ID.
477 270     270   2084 no warnings 'uninitialized'; # $_[0] can be undef in some cases...
  269         410  
  269         62119  
478 242         3796 return split($separator,$_[0])
479             }
480 13         137 };
481             }
482 77         683 Sub::Name::subname('UR::Object::Type::InternalAPI::composite_id_decomposer(closure)',$decomposer);
483 77         215 $self->{get_composite_id_decomposer} = $decomposer;
484             }
485 393         1073 return $decomposer;
486             }
487              
488             sub _resolve_composite_id_separator {
489             # TODO: make the class pull this from its parent at creation time
490             # and only have it dump it if it differs from its parent
491 1390     1434   1722 my $self = shift;
492 1390         1873 my $separator = "\t";
493 1392         5612 for my $class_meta ($self, $self->ancestry_class_metas) {
494 3190 100       11542 if ($class_meta->composite_id_separator) {
495 1390         2846 $separator = $class_meta->composite_id_separator;
496 1392         2228 last;
497             }
498             }
499 1390         2775 return $separator;
500             }
501              
502             sub get_composite_id_resolver {
503 299700     299741 0 233602 my $self = shift;
504 299702         220701 my $resolver;
505 299700 100       531117 unless($resolver = $self->{get_composite_id_resolver}) {
506 2003         6197 my @id_property_names = $self->id_property_names;
507 2004 100       4754 if (@id_property_names == 1) {
508 627     263   2829 $resolver = sub { $_[0] };
  234         432  
509             }
510             else {
511 1377         6668 my $separator = $self->_resolve_composite_id_separator;
512             $resolver = sub {
513 306261 100   306289   447192 if (ref($_[0]) eq 'ARRAY') {
514             # Determine how big the arrayrefs are.
515 2         164 my $underlying_id_count = scalar(@{$_[0]});
  1         3  
516            
517             # We presume that, if one value is an arrayref, the others are also,
518             # and are of equal length.
519 1         2 my @id;
520 2         152 for (my $id_num = 0; $id_num < $underlying_id_count; $id_num++) {
521             # One value per id_property on the class.
522             # Each value is an arrayref in this case.
523 4         4 for my $value (@_) {
524 270     270   1752 no warnings 'uninitialized'; # Some values in the list might be undef
  269         322  
  269         15466  
525 8 100       13 $id[$id_num] .= $separator if $id[$id_num];
526 9         168 $id[$id_num] .= $value->[$id_num];
527             }
528             }
529 1         4 return \@id;
530             }
531             else {
532 270     270   1585 no warnings 'uninitialized'; # Some values in the list might be undef
  269         341  
  269         103007  
533 306260         868656 return join($separator,@_)
534             }
535 1376         8961 };
536             }
537 2004         15915 Sub::Name::subname('UR::Object::Type::InternalAPI::composite_id_resolver(closure)',$resolver);
538 2003         4357 $self->{get_composite_id_resolver} = $resolver;
539             }
540 299700         311859 return $resolver;
541             }
542              
543             # UNUSED, BUT BETTER FOR MULTI-COLUMN FK
544             sub composite_id_list_scalar_mix {
545             # This is like the above, but handles the case of arrayrefs
546             # mixing with scalar values in a multi-property id.
547              
548 1     37 0 151 my ($self, @values) = @_;
549              
550 0         0 my @id_sets;
551 0         0 for my $value (@values) {
552 1 0       157 if (@id_sets == 0) {
553 0 0       0 if (not ref $value) {
554 0         0 @id_sets = ($value);
555             }
556             else {
557 1         188 @id_sets = @$value;
558             }
559             }
560             else {
561 0 0       0 if (not ref $value) {
562 0         0 for my $id_set (@id_sets) {
563 1         167 $id_set .= "\t" . $value;
564             }
565             }
566             else {
567 0         0 for my $new_id (@$value) {
568 0         0 for my $id_set (@id_sets) {
569 1         158 $id_set .= "\t" . $value;
570             }
571             }
572             }
573             }
574             }
575              
576 0 0       0 if (@id_sets == 1) {
577 0         0 return $id_sets[0];
578             }
579             else {
580 1         143 return \@id_sets;
581             }
582             }
583              
584              
585             sub id_property_sorter {
586             # Return a closure that sort can use to sort objects by all their ID properties
587             # This should be the same order that an SQL query with 'order by ...' would return them
588 7794     7824 1 7759 my $self = shift;
589 7794   66     54604 return $self->{'_id_property_sorter'} ||= $self->sorter();
590             }
591              
592             sub sorter {
593 20560     20587 0 29703 my ($self,@properties) = @_;
594 20559         49908 push @properties, $self->id_property_names;
595 20559         42333 my $key = join("__",@properties);
596 20560         36733 my $sorter = $self->{_sorter}{$key};
597 20559 100       39381 unless ($sorter) {
598 835         1152 my @is_numeric;
599             my @is_descending;
600 836         2800 for my $property (@properties) {
601 1425 100       5720 if ($property =~ m/^(-|\+)(.*)$/) {
602 14         51 push @is_descending, $1 eq '-';
603 15         187 $property = $2; # yes, we're manipulating the original list element
604             } else {
605 1411         2107 push @is_descending, 0;
606             }
607              
608 1425         5805 my ($pmeta,@extra) = $self->_concrete_property_meta_for_class_and_name($property);
609 1426 50       3773 if(@extra) {
610             # maybe a composite property (typically ID), or a chained property (prop.other_prop)
611 0         0 $pmeta = $self->property_meta_for_name($property);
612             }
613              
614 1425 100       3429 if ($pmeta) {
    50          
615 894         3559 my $is_numeric = $pmeta->is_numeric;
616 893         2641 push @is_numeric, $is_numeric;
617             }
618             elsif ($UR::initialized) {
619 0         0 Carp::cluck("Failed to find property meta for $property on $self? Cannot produce a sorter for @properties");
620 1         142 push @is_numeric, 0;
621             }
622             else {
623 532         1001 push @is_numeric, 0;
624             }
625             }
626              
627 268     268   1264 no warnings 'uninitialized';
  268         324  
  268         253155  
628             $sorter = $self->{_sorter}{$key} ||= sub($$) {
629              
630 356109     356132   502815 for (my $n = 0; $n < @properties; $n++) {
631 703054         477026 my $property = $properties[$n];
632 703054         721860 my @property_string = split('\.',$property);
633              
634 703055 100       818365 my($first,$second) = $is_descending[$n] ? ($_[1], $_[0]) : ($_[0], $_[1]);
635 703054         523047 for my $current (@property_string) {
636 703054         1060634 $first = $first->$current;
637 703055         985425 $second = $second->$current;
638 703054 100       1199665 if (!defined($second)) {
    100          
639 12         19 return -1;
640             } elsif (!defined($first)) {
641 7         157 return 1;
642             }
643             }
644              
645 703036 100       730549 my $cmp = $is_numeric[$n] ? $first <=> $second : $first cmp $second;
646 703036 100       1326077 return $cmp if $cmp;
647             }
648 687         1421 return 0;
649 835   50     10638 };
650             }
651 20559         54189 Sub::Name::subname("UR::Object::Type::sorter__" . $self->class_name . '__' . $key, $sorter);
652 20559         44493 return $sorter;
653             }
654              
655             sub is_meta {
656 2413     2439 0 1734 my $self = shift;
657 2412         3392 my $class_name = $self->class_name;
658 2412 100       3039 return grep { $_ ne 'UR::Object' and $class_name->isa($_) } keys %meta_classes;
  7237         23146  
659             }
660              
661             sub is_meta_meta {
662 442     462 0 385 my $self = shift;
663 442         866 my $class_name = $self->class_name;
664 443 100       939 return 1 if $meta_classes{$class_name};
665 436         815 return;
666             }
667              
668             # Things that can't safely be removed from the object cache.
669             our %uncachable_types = ( ( map { $_ => 0 } keys %UR::Object::Type::meta_classes), # meta-classes are locked in the cache...
670             'UR::Object' => 1, # .. except for UR::Object
671             'UR::Object::Ghost' => 0,
672             'UR::DataSource' => 0,
673             'UR::Context' => 0,
674             'UR::Object::Index' => 0,
675             );
676             sub is_uncachable {
677 2503     2523 0 2024 my $self = shift;
678              
679 2503         5177 my $class_name = $self->class_name;
680              
681 2503 100       3962 if (@_) {
682             # setting the is_uncachable value
683 6         23 return $uncachable_types{$class_name} = shift;
684             }
685              
686 2497 100       4324 unless (exists $uncachable_types{$class_name}) {
687 145         128 my $is_uncachable = 1;
688 145         738 foreach my $type ( keys %uncachable_types ) {
689 4192 100 100     11388 if ($class_name->isa($type) and ! $uncachable_types{$type}) {
690 20         18 $is_uncachable = 0;
691 20         28 last;
692             }
693             }
694 145         368 $uncachable_types{$class_name} = $is_uncachable;
695 145 50       286 unless (exists $uncachable_types{$class_name}) {
696 0         0 die "Couldn't determine is_uncachable() for $class_name";
697             }
698             }
699 2497         5135 return $uncachable_types{$class_name};
700             }
701              
702              
703             # Mechanisms for generating object IDs when none were specified at
704             # creation time
705              
706             sub autogenerate_new_object_id_uuid {
707 551     568 0 158796 require Data::UUID;
708 551         357601 my $uuid = Data::UUID->new->create_hex();
709 551         2707049 $uuid =~ s/^0x//;
710 551         6065 return $uuid;
711             }
712              
713             our $autogenerate_id_base_format = join(" ",Sys::Hostname::hostname(), "%s", time); # the %s gets $$ when needed
714             our $autogenerate_id_iter = 10000;
715             sub autogenerate_new_object_id_urinternal {
716 643     660 0 919 my($self, $rule) = @_;
717              
718 643         1779 my @id_property_names = $self->id_property_names;
719 643 50       1696 if (@id_property_names > 1) {
720             # we really could, but it seems like if you
721             # asked to do it, it _has_ to be a mistake. If there's a legitimate
722             # reason, this check should be removed
723 0         0 $self->error_message("Can't autogenerate ID property values for multiple ID property class " . $self->class_name);
724 0         0 return;
725             }
726 643         5294 return sprintf($autogenerate_id_base_format, $$) . " " . (++$autogenerate_id_iter);
727             }
728              
729             sub autogenerate_new_object_id_datasource {
730 0     14 0 0 my($self,$rule) = @_;
731              
732 0         0 my ($data_source) = $UR::Context::current->resolve_data_sources_for_class_meta_and_rule($self);
733 0 0       0 if ($data_source) {
734 0         0 return $data_source->autogenerate_new_object_id_for_class_name_and_rule(
735             $self->class_name,
736             $rule
737             );
738             } else {
739 0         0 Carp::croak("Class ".$self->class." has id_generator '-datasource', but the class has no data source to delegate to");
740             }
741             }
742              
743              
744             # Support the autogeneration of unique IDs for objects which require them.
745             sub autogenerate_new_object_id {
746 716     735 1 1395 my $self = _object($_[0]);
747             #my $rule = shift;
748              
749 716 100       2047 unless ($self->{'_resolved_id_generator'}) {
750 246         1453 my $id_generator = $self->id_generator;
751              
752 246 100 100     3008 if (ref($id_generator) eq 'CODE') {
    100          
753 7         29 $self->{'_resolved_id_generator'} = $id_generator;
754              
755             } elsif ($id_generator and $id_generator =~ m/^\-(\S+)/) {
756 211         831 my $id_method = 'autogenerate_new_object_id_' . $1;
757 211         1230 my $subref = $self->can($id_method);
758 211 50       2384 unless ($subref) {
759 0         0 Carp::croak("'$id_generator' is an invalid id_generator for class "
760             . $self->class_name
761             . ": Can't locate object method '$id_method' via package ".ref($self));
762             }
763 211         1029 $self->{'_resolved_id_generator'} = $subref;
764              
765             } else {
766             # delegate to the data source
767 28         103 my ($data_source) = $UR::Context::current->resolve_data_sources_for_class_meta_and_rule($self);
768 28 50       109 if ($data_source) {
769             $self->{'_resolved_id_generator'} = sub {
770 58     75   182 $data_source->autogenerate_new_object_id_for_class_name_and_rule(
771             shift->class_name,
772             shift
773             )
774 28         260 };
775             }
776             }
777             }
778 716         2417 goto $self->{'_resolved_id_generator'};
779             }
780              
781             # from ::Object->generate_support_class
782             our %support_class_suffixes = map { $_ => 1 } qw/Set View Viewer Ghost Iterator Value/;
783             sub generate_support_class_for_extension {
784 2065     2077 0 2221 my $self = shift;
785 2065         2278 my $extension_for_support_class = shift;
786 2065         6874 my $subject_class_name = $self->class_name;
787              
788 2065 50       4455 unless ($subject_class_name) {
789 0         0 Carp::confess("No subject class name for $self?");
790             }
791              
792 2065 50       4181 return unless defined $extension_for_support_class;
793              
794 2065 100       4242 if ($subject_class_name eq "UR::Object") {
795             # Carp::cluck("can't generate $extension_for_support_class for UR::Object!\n");
796             # NOTE: we hit this a bunch of times when "getting" meta-data objects during boostrap.
797 28         34 return;
798             }
799              
800 2037 100       5159 unless ($support_class_suffixes{$extension_for_support_class})
801             {
802             #$self->debug_message("Cannot generate a class with extension $extension_for_support_class.");
803 1630         2562 return;
804             }
805              
806 407         1797 my $subject_class_obj = UR::Object::Type->get(class_name => $subject_class_name);
807 407 50       1432 unless ($subject_class_obj) {
808 0         0 $self->debug_message("Cannot autogenerate $extension_for_support_class because $subject_class_name does not exist.");
809 0         0 return;
810             }
811              
812 407         1329 my $new_class_name = $subject_class_name . "::" . $extension_for_support_class;
813 407         597 my $class_obj;
814 407 50       1523 if ($class_obj = UR::Object::Type->is_loaded($new_class_name)) {
815             # getting the subject class autogenerated the support class automatically
816             # shortcut out
817 0         0 return $class_obj;
818             }
819              
820 266     266   1427 no strict 'refs';
  266         356  
  266         24288  
821 407         744 my @subject_parent_class_names = @{ $subject_class_name . "::ISA" };
  407         2693  
822             my @parent_class_names =
823 268         1009 grep { UR::Object::Type->get(class_name => $_) }
824 268         973 map { $_ . "::" . $extension_for_support_class }
825 268         1709 grep { $_->isa("UR::Object") }
826 407 100       984 grep { $_ !~ /^UR::/ or $extension_for_support_class eq "Ghost" }
  409         4161  
827             @subject_parent_class_names;
828 266     266   1092 use strict 'refs';
  266         325  
  266         188443  
829              
830 407 100       1369 unless (@parent_class_names) {
831 141 50       816 if (UR::Object::Type->get(class_name => ("UR::Object::" . $extension_for_support_class))) {
832 141         583 @parent_class_names = "UR::Object::" . $extension_for_support_class;
833             }
834             }
835              
836 407 50       1369 unless (@parent_class_names) {
837             #print Carp::longmess();
838             #$self->error_message("Cannot autogenerate $extension_for_support_class for $subject_class_name because parent classes (@subject_parent_class_names) do not have classes with that extension.");
839 0         0 return;
840             }
841            
842 407         2550 my @id_property_names = $subject_class_obj->id_property_names;
843 407         889 my %id_property_names = map { $_ => 1 } @id_property_names;
  411         1541  
844            
845 407 100       1438 if ($extension_for_support_class eq 'Ghost') {
846 262         1455 my $subject_class_metaobj = UR::Object::Type->get($self->meta_class_name); # Class object for the subject_class
847 7336         21047 my %class_params = map { $_ => $subject_class_obj->$_ }
848 262   33     1215 grep { my $p = $subject_class_metaobj->property_meta_for_name($_)
  17030         18639  
849             || Carp::croak("Can't no metadata for property '$_' of class ".$self->meta_class_name);
850 17030   100     26075 ! $p->is_delegated and ! $p->is_calculated }
851             $subject_class_obj->__meta__->all_property_names;
852 262         1652 delete $class_params{generated};
853 262         480 delete $class_params{meta_class_name};
854 262         390 delete $class_params{subclassify_by};
855 262         373 delete $class_params{sub_classification_meta_class_name};
856 262         442 delete $class_params{id_generator};
857 262         384 delete $class_params{id};
858 262         373 delete $class_params{is};
859 262         401 delete $class_params{roles};
860              
861 262         1532 my $attributes_have = UR::Util::deep_copy($subject_class_obj->{attributes_have});
862 262         15355 my $class_props = UR::Util::deep_copy($subject_class_obj->{has});
863 262         874312 for (values %$class_props) {
864 15316         10704 delete $_->{class_name};
865 15316         11428 delete $_->{property_name};
866             }
867            
868             %class_params = (
869 262         7033 %class_params,
870             class_name => $new_class_name,
871             is => \@parent_class_names,
872             is_abstract => 0,
873             has => [%$class_props],
874             attributes_have => $attributes_have,
875             id_properties => \@id_property_names,
876             );
877 262         3385 $class_obj = UR::Object::Type->define(%class_params);
878             }
879             else {
880 145         1115 $class_obj = UR::Object::Type->define(
881             class_name => $subject_class_name . "::" . $extension_for_support_class,
882             is => \@parent_class_names,
883             );
884             }
885 407         3342 return $class_obj;
886             }
887              
888             sub has_table {
889 98     112 0 162 my $self = shift;
890 98 50       256 if ($bootstrapping) {
891 0         0 return 0;
892             }
893 98 100       287 return 1 if $self->table_name;
894             # FIXME - shouldn't this call inheritance() instead of parent_classes()?
895 7         85 my @parent_classes = $self->parent_classes;
896 7         18 for my $class_name (@parent_classes) {
897 7 50       20 next if $class_name eq "UR::Object";
898 7         24 my $class_obj = UR::Object::Type->get(class_name => $class_name);
899 7 50       26 if ($class_obj->has_direct_table) {
900 0         0 return 1;
901             }
902             }
903 7         21 return;
904             }
905              
906             sub has_direct_table {
907 146     158 0 190 my $self = shift;
908 146 100       426 return 1 if $self->table_name;
909              
910 69 100 100     248 if ($self->data_source_id and $self->data_source_id->isa('UR::DataSource::Default')) {
911 2         41 my $load_function_name = join('::', $self->class_name, '__load__');
912 2 100       13 return 1 if exists &$load_function_name;
913             }
914 68         242 return;
915             }
916              
917             sub most_specific_subclass_with_table {
918 60     76 0 71 my $self = shift;
919              
920 60 50       148 return $self->class_name if $self->table_name;
921              
922 0         0 foreach my $class_name ( $self->class_name->inheritance ) {
923 0         0 my $class_obj = UR::Object::Type->get(class_name => $class_name);
924 0 0 0     0 return $class_name if ($class_obj and $class_obj->has_direct_table);
925             }
926 0         0 return;
927             }
928              
929             sub most_general_subclass_with_table {
930 0     17 0 0 my $self = shift;
931              
932 0         0 my @subclass_list = reverse ( $self->class_name, $self->class_name->inheritance );
933 0         0 foreach my $class_name ( $self->inheritance ) {
934 0         0 my $class_obj = UR::Object::Type->get(class_name => $class_name);
935 0 0 0     0 return $class_name if ($class_obj && $class_obj->has_direct_table);
936             }
937 0         0 return;
938             }
939              
940            
941              
942             sub _load {
943 53133     53152   53221 my $class = shift;
944 53133         46002 my $rule = shift;
945              
946 53133         89808 $rule = $rule->normalize;
947 53133         108895 my $params = $rule->legacy_params_hash;
948              
949             # While core entity classes are actually loaded,
950             # support classes dynamically generate for them as needed.
951             # Examples are Acme::Employee::View::emp_id, and Acme::Equipment::Ghost
952              
953             # Try to parse the class name.
954 53133         68488 my $class_name = $params->{class_name};
955              
956             # See if the class autogenerates from another class.
957             # i.e.: Acme::Foo::Bar might be generated by Acme::Foo
958 53133 100       87066 unless ($class_name) {
959 2         3 my $namespace = $params->{namespace};
960 2 50       7 if (my $data_source = $params->{data_source_id}) {
961 2         22 $namespace = $data_source->get_namespace;
962             }
963 2 50       5 if ($namespace) {
964             # FIXME This chunk seems to be getting called each time there's a new table/class
965             #Carp::cluck("Getting all classes for namespace $namespace from the filesystem...");
966 2         13 my @classes = $namespace->get_material_classes;
967 2         9 return $class->is_loaded($params);
968             }
969             Carp::confess("Non-class_name used to find a class object: "
970 0 0       0 . join(', ', map { "$_ => " . (defined $params->{$_} ? "'" . $params->{$_} . "'" : 'undef') } keys %$params));
  0         0  
971             }
972              
973             # Besides the common case of asking for a class by its name, the next most
974             # common thing is asking for multiple classes by their names. Rather than doing the
975             # hard work of doing it "right" right here, just recursively call myself with each
976             # item in that list
977 53131 100       93862 if (ref $class_name eq 'ARRAY') {
978             # FIXME is there a more efficient way to add/remove class_name from the rule?
979 269         806 my $rule_without_class_name = $rule->remove_filter('class_name');
980 269         673 $rule_without_class_name = $rule_without_class_name->remove_filter('id'); # id is a synonym for class_name
981 269         488 my @objs = map { $class->_load($rule_without_class_name->add_filter(class_name => $_)) } @$class_name;
  324         822  
982 269         1216 return $class->context_return(@objs);
983             }
984            
985             # If the class is loaded, we're done.
986             # This is an un-documented unique constraint right now.
987 52862         172195 my $class_obj = $class->is_loaded(class_name => $class_name);
988 52862 100       208162 return $class_obj if $class_obj;
989              
990             # Handle deleted classes.
991             # This is written in non-oo notation for bootstrapping.
992 266     266   1323 no warnings;
  266         374  
  266         100376  
993 3772 50 66     31558 if (
      66        
994             $class_name ne "UR::Object::Type::Ghost"
995             and
996             UR::Object::Type::Ghost->can("class")
997             and
998             $UR::Context::current->get_objects_for_class_and_rule("UR::Object::Type::Ghost",$rule,0)
999             ) {
1000 0         0 return;
1001             }
1002              
1003             # Check the filesystem. The file may create its metadata object.
1004 3772         4992 my $exception = do {
1005 3772         4555 local $@;
1006 3772     225   479455 eval "use $class_name";
  223     225   48121  
  0     221   0  
  0     219   0  
  223     216   115524  
  204     2   3101  
  204     2   2168  
  219     2   108178  
  208     2   991  
  208     2   2103  
  217     2   115299  
  209     2   839  
  209     2   2147  
  214     2   104800  
  199     2   492  
  199     2   1904  
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
1007 3772         56297 $@;
1008             };
1009 3772 100       11497 unless ($exception) {
1010             # If the above module was loaded, and is an UR::Object,
1011             # this will find the object. If not, it will return nothing.
1012 2691         10946 $class_obj = $UR::Context::current->get_objects_for_class_and_rule($class,$rule,0);
1013 2691 100       17214 return $class_obj if $class_obj;
1014             }
1015 1092 100       2834 if ($exception) {
1016             # We need to handle $@ here otherwise we'll see
1017             # "Can't locate UR/Object/Type/Ghost.pm in @INC" error.
1018             # We want to fall through "in the right circumstances".
1019 1081         5491 (my $module_path = $class_name . '.pm') =~ s/::/\//g;
1020 1081 100       24921 unless ($exception =~ /Can't locate $module_path in \@INC/) {
1021 4         21 die "Error while autoloading with 'use $class_name': $exception";
1022             }
1023             }
1024              
1025             # Parse the specified class name to check for a suffix.
1026 1088         6040 my ($prefix, $base, $suffix) = ($class_name =~ /^([^\:]+)::(.*)::([^:]+)/);
1027              
1028 1088         1517 my @parts;
1029 1088         4624 ($prefix, @parts) = split(/::/,$class_name);
1030              
1031 1088         4050 for (my $suffix_pos = $#parts; $suffix_pos >= 0; $suffix_pos--)
1032             {
1033 2229         7040 $class_obj = $UR::Context::current->get_objects_for_class_and_rule($class,$rule,0);
1034 2229 50       4760 if ($class_obj) {
1035             # the class was somehow generated while we were checking other classes for it and failing.
1036             # this can happen b/c some class with a name which is a subset of the one we're looking
1037             # for might "use" the one we want.
1038 0 0       0 return $class_obj if $class_obj;
1039             }
1040              
1041 2229         6121 my $base = join("::", @parts[0 .. $suffix_pos-1]);
1042 2229         4763 my $suffix = join("::", @parts[$suffix_pos..$#parts]);
1043              
1044             # See if a class exists for the same name w/o the suffix.
1045             # This may cause this function to be called recursively for
1046             # classes like Acme::Equipment::Set::View::upc_code,
1047             # which would fire recursively for three extensions of
1048             # Acme::Equipment.
1049 2229 100       5711 my $full_base_class_name = $prefix . ($base ? "::" . $base : "");
1050 2229         2124 my $base_class_obj;
1051 2229         2113 my $exception = do {
1052 2229         2271 local $@;
1053 2229         2610 $base_class_obj = eval { $full_base_class_name->__meta__ };
  2229         10548  
1054 2229         5190 $@;
1055             };
1056 2229 50 66     6413 if ($exception && $exception =~ m/^Error while autoloading/) {
1057 0         0 die $exception;
1058             }
1059              
1060 2229 100       5072 if ($base_class_obj)
1061             {
1062             # If so, that class may be able to generate a support
1063             # class.
1064 2065         9612 $class_obj = $full_base_class_name->__extend_namespace__($suffix);
1065 2065 100       7102 if ($class_obj)
1066             {
1067             # Autogeneration worked.
1068             # We still defer to is_loaded, since other parameters
1069             # may prevent the newly "loaded" class from being
1070             # returned.
1071 407         1916 return $UR::Context::current->get_objects_for_class_and_rule($class,$rule,0)
1072             }
1073             }
1074             }
1075              
1076             # If we fall-through to this point, no class was found and no module.
1077 681         3005 return;
1078             }
1079              
1080              
1081             sub use_module_with_namespace_constraints {
1082 266     266   1311 use strict;
  266         335  
  266         5054  
1083 266     266   897 use warnings;
  266         324  
  266         53335  
1084              
1085 7672     7689 0 13409 my $self = shift;
1086 7672         10282 my $target_class = shift;
1087              
1088             # If you do "use Acme; $o = Acme::Rocket->new();", and Perl finds Acme.pm
1089             # at "/foo/bar/Acme.pm", Acme::Rocket must be under /foo/bar/Acme/
1090             # in order to be dynamically loaded.
1091              
1092 7672         28824 my @words = split("::",$target_class);
1093 7672         9438 my $path;
1094 7672         20298 while (@words > 1) {
1095 7357         29584 my $namespace_name = join("::",@words[0..$#words-1]);
1096 7357         22694 my $namespace_expected_module = join("/",@words[0..$#words-1]) . ".pm";
1097              
1098              
1099 7357 100       22318 if ($path = $INC{$namespace_expected_module}) {
1100             #print "got mod $namespace_expected_module at $path for $target_class\n";
1101 7254         120661 $path =~ s/\/*$namespace_expected_module//g;
1102             }
1103             else {
1104 103         479 my $namespace_obj = UR::Object::Type->is_loaded(class_name => $namespace_name);
1105 103 100       466 if ($namespace_obj) {
1106 58         110 eval { $path = $namespace_obj->module_directory };
  58         466  
1107 58 50       192 if ($@) {
1108             # non-module class
1109             # don't auto-use, but don't make a lot of noise about it either
1110             }
1111             }
1112             }
1113 7357 100       22248 last if $path;
1114 73         208 pop @words;
1115             }
1116              
1117 7672 100       16744 unless ($path) {
1118             #Carp::cluck("No module_directory found for namespace $namespace_name."
1119             # . " Cannot dynamically load $target_class.");
1120 388         1441 return;
1121             }
1122              
1123              
1124 7284         22906 $self->_use_safe($target_class,$path);
1125 7284         31407 my $meta = UR::Object::Type->is_loaded(class_name => $target_class);
1126 7284 100       13893 if ($meta) {
1127 7017         19246 return $meta;
1128             }
1129             else {
1130 267         758 return;
1131             }
1132             }
1133              
1134             sub _use_safe {
1135 266     266   1165 use strict;
  266         330  
  266         4882  
1136 266     266   873 use warnings;
  266         313  
  266         535762  
1137              
1138 7284     7299   13070 my ($self, $target_class, $expected_directory) = @_;
1139              
1140             # TODO: use some smart module to determine whether the path is
1141             # relative on the current system.
1142 7284 100 66     46702 if (defined($expected_directory) and $expected_directory !~ /^[\/\\]/) {
1143 2         7 $expected_directory = $pwd_at_compile_time . "/" . $expected_directory;
1144             }
1145              
1146 7284         15902 my $class_path = $target_class . ".pm";
1147 7284         23188 $class_path =~ s/\:\:/\//g;
1148              
1149 7284         34553 my @INC_COPY = @INC;
1150 7284 50       16320 if ($expected_directory) {
1151 7284         15014 unshift @INC, $expected_directory;
1152             }
1153 7284         10159 my $found = "";
1154 7284         12869 for my $dir (@INC) {
1155 12278 100 66     326195 if ($dir and (-e $dir . "/" . $class_path)) {
1156 6867         9671 $found = $dir;
1157 6867         9041 last;
1158             }
1159             }
1160              
1161 7284 100       16210 if (!$found) {
1162             # not found
1163 417         2113 @INC = @INC_COPY;
1164 417         1197 return;
1165             }
1166              
1167 6867 50 33     31134 if ($expected_directory and $expected_directory ne $found) {
1168             # not found in the specified location
1169 0         0 @INC = @INC_COPY;
1170 0         0 return;
1171             }
1172              
1173 6867         7804 do {
1174 6867         34849 local $SIG{__DIE__};
1175 6867         17715 local $SIG{__WARN__};
1176 266     266   1859 eval "use $target_class";
  266     266   625  
  266     266   2376  
  266     266   1987  
  266     266   452  
  266     266   3731  
  266     266   1498  
  266     266   426  
  266     266   1146  
  266     266   1246  
  266     266   391  
  266     257   1477  
  266     246   1498  
  266     437   383  
  266     231   1264  
  266     228   1310  
  266     223   429  
  266     215   2430  
  266     206   1419  
  266     194   433  
  266     367   1639  
  266     2   1510  
  266     1   385  
  266     1   1398  
  266     1   1387  
  266     1   442  
  266     1   2813  
  266     1   1831  
  266     1   400  
  266     1   1484  
  266     1   1702  
  266     1   374  
  266     1   1455  
  257     1   5856  
  257     1   403  
  257     1   1363  
  246     1   6294  
  246         366  
  246         2007  
  437         96315  
  417         837  
  417         2982  
  231         1221  
  231         359  
  231         1446  
  228         1394  
  228         341  
  228         984  
  223         2388  
  223         335  
  223         953  
  215         2051  
  215         310  
  215         884  
  206         1512  
  206         301  
  206         1194  
  194         1864  
  194         350  
  194         967  
  367         77644  
  342         772  
  342         2520  
  6867         868094  
1177             };
1178              
1179             # FIXME - if the use above failed because of a compilation error in the module we're trying to
1180             # load, then the error message below just tells the user that "Compilation failed in require"
1181             # and isn't propogating the error message about what caused the compile to fail
1182 6867 50       120297 if ($@) {
1183             #local $SIG{__DIE__};
1184              
1185 0         0 @INC = @INC_COPY;
1186 0         0 die ("ERROR DYNAMICALLY LOADING CLASS $target_class\n$@");
1187             }
1188              
1189 6867         24946 for (0..$#INC) {
1190 6867 50       19217 if ($INC[$_] eq $expected_directory) {
1191 6867         15306 splice @INC, $_, 1;
1192 6867         10513 last;
1193             }
1194             }
1195              
1196 6867         17404 return 1;
1197             }
1198              
1199              
1200             # sub _object
1201             # This is used to make sure that methods are called
1202             # as object methods and not class methods.
1203             # The typical case that's important is when something
1204             # like UR::Object::Type->method(...) is called.
1205             # If an object is expected in a method and it gets
1206             # a class instead, well, unpredictable things can
1207             # happen.
1208             #
1209             # For many methods on UR::Objects, the implementation
1210             # is in UR::Object. However, some of those methods
1211             # have the same name as methods in here (purposefully),
1212             # and those UR::Object methods often get the
1213             # UR::Object::Type object and call the same method,
1214             # which ends up in this file. The problem is when
1215             # those methods are called on UR::Object::Type
1216             # itself it come directly here, without getting
1217             # the UR::Object::Type object for UR::Object::Type
1218             # (confused yet?). So to fix this, we use _object to
1219             # make sure we have an object and not a class.
1220             #
1221             # Basically, we make sure we're working with a class
1222             # object and not a class name.
1223             #
1224              
1225             sub _object {
1226 98466 50   98480   183763 return ref($_[0]) ? $_[0] : $_[0]->__meta__;
1227             }
1228              
1229             # new version gets everything, including "id" itself and object ref properties
1230             push @cache_keys, '_all_property_type_names';
1231             sub all_property_type_names {
1232 2567     2577 0 3493 my $self = shift;
1233            
1234 2567 50       6907 if ($self->{_all_property_type_names}) {
1235 0         0 return @{ $self->{_all_property_type_names} };
  0         0  
1236             }
1237            
1238             #my $rule_template = UR::BoolExpr::Template->resolve('UR::Object::Type', 'id');
1239              
1240 2567         6526 my $all_property_type_names = $self->{_all_property_type_names} = [];
1241 2567         9784 for my $class_name ($self->class_name, $self->ancestry_class_names) {
1242 7309         18637 my $class_meta = UR::Object::Type->get($class_name);
1243             #my $rule = $rule_template->get_rule_for_values($class_name);
1244             #my $class_meta = $UR::Context::current->get_objects_for_class_and_rule('UR::Object::Type',$rule);
1245 7309 50       15045 if (my $has = $class_meta->{has}) {
1246 7309         51534 push @$all_property_type_names, sort keys %$has;
1247             }
1248             }
1249 2567         10756 return @$all_property_type_names;
1250             }
1251              
1252             sub table_for_property {
1253 114     124 0 256 my $self = _object(shift);
1254 114 50       262 Carp::croak('must pass a property_name to table_for_property') unless @_;
1255 114         122 my $property_name = shift;
1256 114         358 for my $class_object ( $self, $self->ancestry_class_metas )
1257             {
1258 123         382 my $property_object = UR::Object::Property->get( class_name => $class_object->class_name, property_name => $property_name );
1259 123 100       302 if ( $property_object )
1260             {
1261 117 100       335 next unless $property_object->column_name;
1262 114         354 return $class_object->table_name;
1263             }
1264             }
1265              
1266 0         0 return;
1267             }
1268              
1269             sub column_for_property {
1270 2046     2059 0 2445 my $self = _object(shift);
1271 2046 50       2943 Carp::croak('must pass a property_name to column_for_property') unless @_;
1272 2046         1877 my $property_name = shift;
1273              
1274 2046         1338 my($properties,$columns) = @{$self->{'_all_properties_columns'}};
  2046         2214  
1275 2046         3245 for (my $i = 0; $i < @$properties; $i++) {
1276 4422 100       6959 if ($properties->[$i] eq $property_name) {
1277 1970         4543 return $columns->[$i];
1278             }
1279             }
1280              
1281 76         106 for my $class_object ( $self->ancestry_class_metas ) {
1282 56         90 my $column_name = $class_object->column_for_property($property_name);
1283 56 50       82 return $column_name if $column_name;
1284             }
1285 76         97 return;
1286             }
1287              
1288             sub property_for_column {
1289 5230     5248 0 19548 my $self = _object(shift);
1290 5230 50       9728 Carp::croak('must pass a column_name to property_for_column') unless @_;
1291 5230         7993 my $column_name = lc(shift);
1292              
1293 5230   50     9478 my $data_source = $self->data_source || 'UR::DataSource';
1294 5230         5257 my($table_name,$self_table_name);
1295 5230         12337 ($table_name, $column_name) = $data_source->_resolve_table_and_column_from_column_name($column_name);
1296 5230         13904 (undef, $self_table_name) = $data_source->_resolve_owner_and_table_from_table_name($self->table_name);
1297              
1298 5230 100 66     22727 if (! $table_name) {
    100 100        
    50          
1299 3040         2935 my($properties,$columns) = @{$self->{'_all_properties_columns'}};
  3040         5596  
1300 3040         7809 for (my $i = 0; $i < @$columns; $i++) {
1301 7840 100       17186 if (lc($columns->[$i]) eq $column_name) {
1302 3030         6981 return $properties->[$i];
1303             }
1304             }
1305             } elsif ($table_name
1306             and
1307             $self_table_name
1308             and lc($self_table_name) eq lc($table_name)
1309             ) {
1310             # @$properties and @$columns contain items inherited from parent classes
1311             # make sure the property we find with that name goes to this class
1312 2148         5081 my $property_name = $self->property_for_column($column_name);
1313 2148 100       4308 return undef unless $property_name;
1314 2146         5010 my $prop_meta = $self->property_meta_for_name($property_name);
1315 2146 100 100     6405 if ($prop_meta->class_name eq $self->class_name
1316             and
1317             lc($prop_meta->column_name) eq $column_name
1318             ) {
1319 2144         6325 return $property_name;
1320             }
1321              
1322             } elsif ($table_name) {
1323              
1324 42         174 for my $class_object ( $self, $self->ancestry_class_metas ) {
1325 92 100       194 next unless $class_object->data_source;
1326 80         92 my $class_object_table_name;
1327 80         140 (undef, $class_object_table_name)
1328             = $class_object->data_source->_resolve_owner_and_table_from_table_name($class_object->table_name);
1329              
1330 80 100 100     395 if (! $class_object_table_name
1331             or
1332             $table_name ne lc($class_object_table_name)
1333             ) {
1334 44         103 (undef, $class_object_table_name) = $class_object->data_source->parse_view_and_alias_from_inline_view($class_object->table_name);
1335             }
1336 80 100 100     343 next if (! $class_object_table_name
1337             or
1338             $table_name ne lc($class_object_table_name));
1339              
1340 40         166 my $property_name = $class_object->property_for_column($column_name);
1341 40 100       194 return $property_name if $property_name;
1342             }
1343             }
1344              
1345 18         45 return;
1346             }
1347              
1348             # Methods for maintaining unique constraints
1349             # This is primarily used by the class re-writer (ur update classes-from-db), but
1350             # BoolExprs use them,too
1351              
1352             # Adds a constraint by name and property list to the class metadata. The class initializer
1353             # fills this data in via the 'constraints' key, so it shouldn't call add_unique_constraint()
1354             # directly
1355             sub add_unique_constraint {
1356 0     16 1 0 my $self = shift;
1357              
1358 0 0       0 unless (@_) {
1359 0         0 Carp::croak('method add_unique_constraint requires a constraint name as a parameter');
1360             }
1361 0         0 my $constraint_name = shift;
1362              
1363 0         0 my $constraints = $self->unique_property_set_hashref();
1364 0 0       0 if (exists $constraints->{$constraint_name}) {
1365 0         0 Carp::croak("A constraint named '$constraint_name' already exists for class ".$self->class_name);
1366             }
1367              
1368 0 0       0 unless (@_) {
1369 0         0 Carp::croak('method add_unique_constraint requires one or more property names as parameters');
1370             }
1371 0         0 my @property_names = @_;
1372              
1373             # Add a new constraint record
1374 0         0 push @{ $self->{'constraints'} } , { sql => $constraint_name, properties => \@property_names };
  0         0  
1375             # invalidate the other cached data
1376 0         0 $self->_invalidate_cached_data_for_subclasses('_unique_property_sets', '_unique_property_set_hashref');
1377             }
1378              
1379             sub remove_unique_constraint {
1380 0     14 1 0 my $self = shift;
1381              
1382 0 0       0 unless (@_) {
1383 0         0 Carp::croak("method remove_unique_constraint requires a constraint name as a parameter");
1384             }
1385              
1386 0         0 my $constraint_name = shift;
1387 0         0 my $constraints = $self->unique_property_set_hashref();
1388 0 0       0 unless (exists $constraints->{$constraint_name}) {
1389 0         0 Carp::croak("There is no constraint named '$constraint_name' for class ".$self->class_name);
1390             }
1391              
1392             # Remove the constraint record
1393 0         0 for (my $i = 0; $i < @{$self->{'constraints'}}; $i++) {
  0         0  
1394 0 0       0 if ($self->{'constraints'}->[$i]->{'sql'} = $constraint_name) {
1395 0         0 splice(@{$self->{'constraints'}}, $i, 1);
  0         0  
1396             }
1397             }
1398 0         0 $self->_invalidate_cached_data_for_subclasses('_unique_property_sets', '_unique_property_set_hashref');
1399             }
1400              
1401              
1402             # This returns a list of lists. Each inner list is the properties/columns
1403             # involved in the constraint
1404             sub unique_property_sets {
1405 1673     1683 0 1882 my $self = shift;
1406 1673 100       3891 if ($self->{_unique_property_sets}) {
1407 1240         1286 return @{ $self->{_unique_property_sets} };
  1240         4117  
1408             }
1409              
1410 433         1282 my $unique_property_sets = $self->{_unique_property_sets} = [];
1411              
1412 433         1540 for my $class_name ($self->class_name, $self->ancestry_class_names) {
1413 1362         3308 my $class_meta = UR::Object::Type->get($class_name);
1414 1362 50       2962 if ($class_meta->{constraints}) {
1415 1362         1221 for my $spec (@{ $class_meta->{constraints} }) {
  1362         2644  
1416 0         0 push @$unique_property_sets, [ @{ $spec->{properties} } ]
  0         0  
1417             }
1418             }
1419             }
1420 433         1434 return @$unique_property_sets;
1421             }
1422              
1423             # Return the constraint information as a hashref
1424             # keys are the SQL constraint name, values are a listref of property/column names involved
1425             sub unique_property_set_hashref {
1426 8     20 1 17 my $self = shift;
1427              
1428 8 50       28 if ($self->{_unique_property_set_hashref}) {
1429 0         0 return $self->{_unique_property_set_hashref};
1430             }
1431              
1432 8         21 my $unique_property_set_hashref = $self->{_unique_property_set_hashref} = {};
1433            
1434 8         31 for my $class_name ($self->class_name, $self->ancestry_class_names) {
1435 51         115 my $class_meta = UR::Object::Type->get($class_name);
1436 51 50       107 if ($class_meta->{'constraints'}) {
1437 51         37 for my $spec (@{ $class_meta->{'constraints'} }) {
  51         82  
1438 0         0 my $unique_group = $spec->{'sql'};
1439 0 0       0 next if ($unique_property_set_hashref->{$unique_group}); # child classes override parents
1440 0         0 $unique_property_set_hashref->{$unique_group} = [ @{$spec->{properties}} ];
  0         0  
1441             }
1442             }
1443             }
1444              
1445 8         25 return $unique_property_set_hashref;
1446             }
1447              
1448              
1449             # Used by the class meta meta data constructors to make changes in the
1450             # raw data stored in the class object's hash. These should really
1451             # only matter while running ur update
1452              
1453             # Args are:
1454             # 1) An UR::Object::Property object with attribute_name, class_name, id, property_name, type_name
1455             # 2) The method called: _construct_object, load,
1456             # 3) An id?
1457             sub _property_change_callback {
1458 46974     46986   46459 my($property_obj,$method, $old_val, $new_val) = @_;
1459              
1460 46974 100 66     141847 return if ($method eq 'load' || $method eq 'unload');
1461 3 50       8 return unless ref($property_obj); # happens when, say, error_message is called on the UR::Object::Property class
1462              
1463 3         11 my $class_obj = UR::Object::Type->get(class_name => $property_obj->class_name);
1464 3         12 my $property_name = $property_obj->property_name;
1465              
1466 3 100       16 $old_val = '' unless(defined $old_val);
1467 3 50       8 $new_val = '' unless(defined $new_val);
1468              
1469 3 100 33     18 if ($method eq 'create') {
    50          
    50          
1470 2 50       6 unless ($class_obj->{'has'}->{$property_name}) {
1471 2         7 my @attr = qw( class_name data_length data_type is_delegated is_optional property_name );
1472              
1473 2         2 my %new_property;
1474 2         4 foreach my $attr_name (@attr ) {
1475 12         19 $new_property{$attr_name} = $property_obj->$attr_name();
1476             }
1477 2         4 $class_obj->{'has'}->{$property_name} = \%new_property;
1478             }
1479 2 50       4 if (defined $property_obj->is_id) {
1480 0         0 &_id_property_change_callback($property_obj, 'create');
1481             }
1482              
1483             } elsif ($method eq 'delete') {
1484 0 0       0 if (defined $property_obj->is_id) {
1485 0         0 &_id_property_change_callback($property_obj, 'delete');
1486             }
1487 0         0 delete $class_obj->{'has'}->{$property_name};
1488              
1489             } elsif ($method eq 'is_id' and $new_val ne $old_val) {
1490 0 0       0 my $change = $new_val ? 'create' : 'delete';
1491 0         0 &_id_property_change_callback($property_obj, $change);
1492             }
1493              
1494 3 50 33     19 if (exists $class_obj->{'has'}->{$property_name}
1495             && exists $class_obj->{'has'}->{$property_name}->{$method}) {
1496 0         0 $class_obj->{'has'}->{$property_name}->{$method} = $new_val;
1497              
1498             }
1499              
1500             # Invalidate the cache used by all_property_names()
1501 3         7 for my $key (@cache_keys) {
1502 27         57 $class_obj->_invalidate_cached_data_for_subclasses($key);
1503             }
1504             }
1505              
1506              
1507             # Some expensive-to-calculate data gets stored in the class meta hashref
1508             # and needs to be removed for all the existing subclasses
1509             sub _invalidate_cached_data_for_subclasses {
1510 27     36   39 my($class_meta, @cache_keys) = @_;
1511              
1512 27         53 delete @$class_meta{@cache_keys};
1513              
1514 27         19 my @subclasses = @{$UR::Object::Type::_init_subclasses_loaded{$class_meta->class_name}};
  27         48  
1515 27         23 my %seen;
1516 27         57 while (my $subclass = shift @subclasses) {
1517 9 50       20 next if ($seen{$subclass}++);
1518 9         24 my $sub_meta = UR::Object::Type->get(class_name => $subclass);
1519 9         21 delete @$sub_meta{@cache_keys};
1520 9         7 push @subclasses, @{$UR::Object::Type::_init_subclasses_loaded{$sub_meta->class_name}};
  9         20  
1521             }
1522             }
1523              
1524              
1525             # A streamlined version of the method just below that dosen't check that the
1526             # data in both places is the same before a delete operation. What was happening
1527             # was that an ID property got deleted and the position checks out ok, but then
1528             # a second ID property gets deleted and now the position dosen't match because we
1529             # aren't able to update the object's position property 'cause it's an ID property
1530             # and can't be changed.
1531             #
1532             # The short story is that we've lowered the bar for making sure it's safe to delete info
1533             sub _id_property_change_callback {
1534 0     9   0 my $property_obj = shift;
1535 0         0 my $method = shift;
1536              
1537 0 0 0     0 return if ($method eq 'load' || $method eq 'unload');
1538              
1539 0         0 my $class = UR::Object::Type->get(class_name => $property_obj->class_name);
1540            
1541 0 0       0 if ($method eq 'create') {
    0          
1542 0         0 my $pos = $property_obj->id_by;
1543 0         0 $pos += 0; # make sure it's a number
1544 0 0       0 if ($pos <= @{$class->{'id_by'}}) {
  0         0  
1545 0         0 splice(@{$class->{'id_by'}}, $pos, 0, $property_obj->property_name);
  0         0  
1546             } else {
1547             # $pos is past the end... probably an id property was deleted and another added
1548 0         0 push @{$class->{'id_by'}}, $property_obj->property_name;
  0         0  
1549             }
1550             } elsif ($method eq 'delete') {
1551 0         0 my $property_name = $property_obj->property_name;
1552 0         0 for (my $i = 0; $i < @{$class->{'id_by'}}; $i++) {
  0         0  
1553 0 0       0 if ($class->{'id_by'}->[$i] eq $property_name) {
1554 0         0 splice(@{$class->{'id_by'}}, $i, 1);
  0         0  
1555 0         0 return;
1556             }
1557             }
1558             #$DB::single = 1;
1559 0         0 Carp::confess("Internal data consistancy problem: could not find property named $property_name in id_by list for class meta " . $class->class_name);
1560              
1561             } else {
1562             # Shouldn't get here since ID properties can't be changed, right?
1563             #$DB::single = 1;
1564 0         0 Carp::confess("Shouldn't be here as ID properties can't change");
1565 0         0 1;
1566             }
1567              
1568 0         0 $class->{'_all_id_property_names'} = undef; # Invalidate the cache used by all_id_property_names
1569             }
1570              
1571              
1572             #
1573             # BOOTSTRAP CODE
1574             #
1575              
1576             sub get_with_special_parameters {
1577 0     10 0 0 my $class = shift;
1578 0         0 my $rule = shift;
1579 0         0 my %extra = @_;
1580 0 0       0 if (my $namespace = delete $extra{'namespace'}) {
1581 0 0       0 unless (keys %extra) {
1582 0         0 my @c = $namespace->get_material_classes();
1583 0         0 @c = grep { $_->namespace eq $namespace } $class->is_loaded($rule->params_list);
  0         0  
1584 0         0 return $class->context_return(@c);
1585             }
1586             }
1587 0         0 return $class->SUPER::get_with_special_parameters($rule,@_);
1588             }
1589              
1590             sub __signal_change__ {
1591 24689     24697   27531 my $self = shift;
1592 24689         81477 my @rv = $self->SUPER::__signal_change__(@_);
1593 24689 50       52152 if ($_[0] eq "delete") {
1594 0         0 my $class_name = $self->{class_name};
1595 0         0 $self->ungenerate();
1596             }
1597 24689         37919 return @rv;
1598             }
1599              
1600             my @default_valid_signals = qw(create delete commit rollback load unload load_external subclass_loaded);
1601             our %STANDARD_VALID_SIGNALS;
1602             @STANDARD_VALID_SIGNALS{@default_valid_signals} = (1) x @default_valid_signals;
1603             sub _is_valid_signal {
1604 739     749   1028 my $self = shift;
1605 739         1073 my $aspect = shift;
1606              
1607             # An aspect of empty string (or undef) means all aspects are being observed.
1608 739 100 66     4851 return 1 unless (defined($aspect) and length($aspect));
1609              
1610             # All standard creation and destruction methods emit a signal.
1611 372 100       1091 return 1 if ($STANDARD_VALID_SIGNALS{$aspect});
1612              
1613 311         1093 for my $property ($self->all_property_names)
1614             {
1615 2941 100       3641 return 1 if $property eq $aspect;
1616             }
1617              
1618 294 100       736 if (!exists $self->{'_is_valid_signal'}) {
1619 145         156 $self->{'_is_valid_signal'} = { map { $_ => 1 } @{$self->{'valid_signals'}} };
  294         524  
  145         394  
1620             }
1621              
1622 294 100       817 return 1 if ($self->{'_is_valid_signal'}->{$aspect});
1623              
1624 215         1208 foreach my $parent_meta ( $self->parent_class_metas ) {
1625 211 100       1307 if ($parent_meta->_is_valid_signal($aspect)) {
1626 168         310 $self->{'_is_valid_signal'}->{$aspect} = 1;
1627 168         498 return 1;
1628             }
1629             }
1630              
1631 47         116 return 0;
1632             }
1633              
1634              
1635             sub generated {
1636 78126     78136 0 80631 my $self = shift;
1637 78126 100       144923 if (@_) {
1638 24688         37183 $self->{'generated'} = shift;
1639             }
1640 78126         173892 return $self->{'generated'};
1641             }
1642              
1643             sub ungenerate {
1644 0     8 0 0 my $self = shift;
1645 0         0 my $class_name = $self->class_name;
1646 0         0 delete $UR::Object::_init_subclass->{$class_name};
1647 0         0 delete $UR::Object::Type::_inform_all_parent_classes_of_newly_loaded_subclass{$class_name};
1648 0         0 do {
1649 266     266   1418 no strict;
  266         391  
  266         5952  
1650 266     266   909 no warnings;
  266         386  
  266         123200  
1651             my @symbols_which_are_not_subordinate_namespaces =
1652 0         0 grep { substr($_,-2) ne '::' }
1653 0         0 keys %{ $class_name . "::" };
  0         0  
1654 0         0 my $hr = \%{ $class_name . "::" };
  0         0  
1655 0         0 delete @$hr{@symbols_which_are_not_subordinate_namespaces};
1656             };
1657 0         0 my $module_name = $class_name;
1658 0         0 $module_name =~ s/::/\//g;
1659 0         0 $module_name .= ".pm";
1660 0         0 delete $INC{$module_name};
1661 0         0 $self->{'generated'} = 0;
1662             }
1663              
1664             sub singular_accessor_name_for_is_many_accessor {
1665 19147     19154 1 17059 my($self, $property_name) = @_;
1666 19147 100       47373 unless (exists $self->{_accessor_singular_names}->{$property_name}) {
1667 2736 100       7468 my $property_meta = $self->property_meta_for_name($property_name) if ($self->generated);
1668 2736 100 100     9633 if ($bootstrapping # trust the caller when bootstrapping
      66        
      66        
1669             or
1670             ! $self->generated # when called from UR::Object::Type::AccessorWriter and the property isn't created yet
1671             or
1672             ($property_meta && $property_meta->is_many)
1673             ) {
1674 2734         16586 require Lingua::EN::Inflect;
1675 2734         9200 $self->{_accessor_singular_names}->{$property_name} = Lingua::EN::Inflect::PL_V($property_name);
1676             } else {
1677 2         4 $self->{_accessor_singular_names}->{$property_name} = undef;
1678             }
1679             }
1680 19147         389596 return $self->{_accessor_singular_names}->{$property_name};
1681             }
1682              
1683             sub iterator_accessor_name_for_is_many_accessor {
1684 2735     2742 1 3545 my($self, $property_name) = @_;
1685              
1686 2735         4466 my $singular = $self->singular_accessor_name_for_is_many_accessor($property_name);
1687 2735   33     10672 return $singular && "${singular}_iterator";
1688             }
1689              
1690             sub set_accessor_name_for_is_many_accessor {
1691 2735     2741 1 3581 my($self, $property_name) = @_;
1692              
1693 2735         4506 my $singular = $self->singular_accessor_name_for_is_many_accessor($property_name);
1694 2735   33     10963 return $singular && "${singular}_set";
1695             }
1696              
1697             sub rule_accessor_name_for_is_many_accessor {
1698 2735     2738 1 3691 my($self, $property_name) = @_;
1699              
1700 2735         5092 my $singular = $self->singular_accessor_name_for_is_many_accessor($property_name);
1701 2735   33     12030 return $singular && "__${singular}_rule";
1702             }
1703              
1704             sub arrayref_accessor_name_for_is_many_accessor {
1705 2735     2737 1 3672 my($self, $property_name) = @_;
1706              
1707 2735         4781 my $singular = $self->singular_accessor_name_for_is_many_accessor($property_name);
1708 2735   33     11015 return $singular && "${singular}_arrayref";
1709             }
1710              
1711             sub adder_name_for_is_many_accessor {
1712 2735     2737 1 3616 my($self, $property_name) = @_;
1713              
1714 2735         4558 my $singular = $self->singular_accessor_name_for_is_many_accessor($property_name);
1715 2735   33     11287 return $singular && "add_${singular}";
1716             }
1717              
1718             sub remover_name_for_is_many_accessor {
1719 2735     2737 1 3604 my($self, $property_name) = @_;
1720              
1721 2735         4895 my $singular = $self->singular_accessor_name_for_is_many_accessor($property_name);
1722 2735   33     11132 return $singular && "remove_${singular}";
1723             }
1724              
1725             1;
1726