File Coverage

lib/UR/Object/Type/Initializer.pm
Criterion Covered Total %
statement 1031 1128 91.4
branch 641 734 87.3
condition 436 744 58.6
subroutine 332 332 100.0
pod 77 77 100.0
total 2517 3015 83.4


line stmt bran cond sub pod time code
1             # This line forces correct deployment by some tools.
2             package UR::Object::Type::Initializer;
3              
4             package UR::Object::Type;
5              
6 273     273   1247 use strict;
  270         416  
  272         8071  
7 272     272   1043 use warnings;
  270         405  
  270         9855  
8             require UR;
9              
10 274     270   1087 use UR::Util;
  271         381  
  269         1584  
11              
12             BEGIN {
13             # Perl 5.10 did not require mro in order to call get_mro but it looks
14             # like that was "fixed" in newer version.
15 268 100   273   14032 if ($^V ge v5.9.5) {
16 268         15172 eval "require mro";
17             }
18             };
19              
20             our $VERSION = "0.46"; # UR $VERSION;
21              
22 268     268   195098 use Carp ();
  272         465  
  269         3286  
23 269     268   991 use Sub::Name ();
  269         370  
  269         2969  
24 269     271   870 use Sub::Install ();
  268         370  
  268         119531  
25              
26             # keys are class property names (like er_role, is_final, etc) and values are
27             # the default value to use if it's not specified in the class definition
28             #
29             # For most classes, this kind of thing is handled by the default_value attribute on
30             # a class' property. For bootstrapping reasons, the default values for the
31             # properties of UR::Object::Type' class need to be listed here as well. If
32             # any of these change, or new default valued items are added, be sure to also
33             # update the class definition for UR::Object::Type (which really lives in UR.pm
34             # for the moment)
35             %UR::Object::Type::defaults = (
36             er_role => 'entity',
37             is_final => 0,
38             is_singleton => 0,
39             is_transactional => 1,
40             is_mutable => 1,
41             is_many => 0,
42             is_abstract => 0,
43             subclassify_by_version => 0,
44             );
45              
46             # All those same comments also apply to UR::Object::Property's properties
47             %UR::Object::Property::defaults = (
48             is_optional => 0,
49             is_transient => 0,
50             is_constant => 0,
51             is_volatile => 0,
52             is_classwide => 0,
53             is_delegated => 0,
54             is_calculated => 0,
55             is_mutable => undef,
56             is_transactional => 1,
57             is_many => 0,
58             is_numeric => 0,
59             is_specified_in_module_header => 0,
60             is_deprecated => 0,
61             position_in_module_header => -1,
62             doc_position => -1,
63             is_undocumented => 0,
64             );
65              
66             @UR::Object::Type::meta_id_ref_shared_properties = (
67             qw/
68             is_optional
69             is_transient
70             is_constant
71             is_volatile
72             is_classwide
73             is_transactional
74             is_abstract
75             is_concrete
76             is_final
77             is_many
78             is_deprecated
79             is_undocumented
80             /
81             );
82              
83             %UR::Object::Type::converse = (
84             required => 'optional',
85             abstract => 'concrete',
86             one => 'many',
87             );
88              
89             # These classes are used to define an object class.
90             # As such, they get special handling to bootstrap the system.
91              
92             our %meta_classes = map { $_ => 1 }
93             qw/
94             UR::Object
95             UR::Object::Type
96             UR::Object::Property
97             /;
98              
99             our $bootstrapping = 1;
100             our @partially_defined_classes;
101              
102             # When copying the object hash to create its db_committed, these keys should be removed because
103             # they contain things like coderefs
104             our @keys_to_delete_from_db_committed = qw( id db_committed _id_property_sorter get_composite_id_resolver get_composite_id_decomposer );
105              
106             # Stages of Class Initialization
107             #
108             # define() is called to indicate the class structure (create() may also be called by the db sync command to make new classes)
109             #
110             # the parameters to define()/create() are normalized by _normalize_class_description()
111             #
112             # a basic functional class meta object is created by _define_minimal_class_from_normalized_class_description()
113             #
114             # accessors are created
115             #
116             # if we're still bootstrapping:
117             #
118             # the class is stashed in an array so the post-bootstrapping stages can be done in bulk
119             #
120             # we exit define()
121             #
122             # if we're done bootstrapping:
123             #
124             # _inform_all_parent_classes_of_newly_loaded_subclass() sets up an internal map of known subclasses of each base class
125             #
126             # _complete_class_meta_object_definitions() decomposes the definition into normalized objects
127             #
128              
129             sub __define__ {
130 24730     24735   34960 my $class = shift;
131 24730         63453 my $desc = $class->_normalize_class_description(@_);
132              
133 24694   66     55959 my $class_name = $desc->{class_name} ||= (caller(0))[0];
134 24694         27293 $desc->{class_name} = $class_name;
135              
136 24694         20954 my $self;
137              
138 24694         53565 my %params = $class->_construction_params_for_desc($desc);
139 24694         25596 my $meta_class_name;
140 24694 100       36198 if (%params) {
141 11836         36950 $self = __PACKAGE__->__define__(%params);
142 11836 50       26940 return unless $self;
143 11836         18697 $meta_class_name = $params{class_name};
144             }
145             else {
146 12858         13938 $meta_class_name = __PACKAGE__;
147             }
148              
149 24694         47942 $self = $UR::Context::all_objects_loaded->{$meta_class_name}{$class_name};
150 24694 100       41540 if ($self) {
151             #$DB::single = 1;
152             #Carp::cluck("Re-defining class $class_name? Found $meta_class_name with id '$class_name'");
153 6         35 return $self;
154             }
155              
156 24688         58156 $self = $class->_make_minimal_class_from_normalized_class_description($desc);
157 24688 50       117967 Carp::confess("Failed to define class $class_name!") unless $self;
158              
159             # we do this for define() but not create()
160 24688         189927 my %db_committed = %$self;
161 24688         79063 delete @db_committed{@keys_to_delete_from_db_committed};
162 24688         40198 $self->{'db_committed'} = \%db_committed;
163              
164 24688 50       95096 $self->_initialize_accessors_and_inheritance
165             or Carp::confess("Error initializing accessors for $class_name!");
166              
167 24688 100       81832 if ($bootstrapping) {
168 11966         17224 push @partially_defined_classes, $self;
169             }
170             else {
171 12722 100       59937 unless ($self->_inform_all_parent_classes_of_newly_loaded_subclass()) {
172 0         0 Carp::confess(
173             "Failed to link to parent classes to complete definition of class $class_name!"
174             . $class->error_message
175             );
176             }
177 12722 50       57173 unless ($self->_complete_class_meta_object_definitions()) {
178             #$DB::single = 1;
179 0         0 $self->_complete_class_meta_object_definitions();
180 0         0 Carp::confess(
181             "Failed to complete definition of class $class_name!"
182             . $class->error_message
183             );
184             }
185             }
186              
187 24686         107029 $self->_inform_roles_of_new_class();
188              
189 24686         219668 return $self;
190             }
191              
192              
193             sub create {
194             # this is typically only used by code which intendes to autogenerate source code
195             # it will lead to the writing of a Perl module upon commit.
196 2     3 1 728 my $class = shift;
197 2         9 my $desc = $class->_normalize_class_description(@_);
198              
199 2   66     6 my $class_name = $desc->{class_name} ||= (caller(0))[0];
200 2         4 my $meta_class_name = $desc->{meta_class_name};
201              
202 280     268   1333 no strict 'refs';
  267         422  
  267         52694  
203 2 100 66     6 unless (
      66        
204             $meta_class_name eq __PACKAGE__
205             or
206             # in newer Perl interpreters the ->isa() call can return true
207             # even if @ISA has been emptied (OS X) ???
208 1         7 (scalar(@{$meta_class_name . '::ISA'}) and $meta_class_name->isa(__PACKAGE__))
209             ) {
210 1 50       5 if (__PACKAGE__->get(class_name => $meta_class_name)) {
211 0         0 warn "class $meta_class_name already exists when creating class meta for $class_name?!";
212             }
213             else {
214 1         6 __PACKAGE__->create(
215             __PACKAGE__->_construction_params_for_desc($desc)
216             );
217             }
218             }
219              
220 2         7 my $self = $class->_make_minimal_class_from_normalized_class_description($desc);
221 2 50       9 Carp::confess("Failed to define class $class_name!") unless $self;
222              
223 2 50       9 $self->_initialize_accessors_and_inheritance
224             or Carp::confess("Failed to define class $class_name!");
225              
226 2 100       16 $self->_inform_all_parent_classes_of_newly_loaded_subclass()
227             or Carp::confess(
228             "Failed to link to parent classes to complete definition of class $class_name!"
229             . $class->error_message
230             );
231              
232 2         9 $self->generated(0);
233              
234 2         13 $self->__signal_change__("create");
235              
236 2         10 return $self;
237             }
238              
239             sub _preprocess_subclass_description {
240             # allow a class to modify the description of any subclass before it instantiates
241             # this filtering allows a base class to specify policy, add meta properties, etc.
242 38908     38909   35953 my ($self,$prev_desc) = @_;
243              
244 38908         31044 my $current_desc = $prev_desc;
245              
246 38908 100       88352 if (my $preprocessor = $self->subclass_description_preprocessor) {
247             # the preprocessor must me a method name in the class being adjusted
248 267     268   1194 no strict 'refs';
  267         430  
  267         296665  
249 29 100       139 unless ($self->class_name->can($preprocessor)) {
250 0         0 die "Class " . $self->class_name
251             . " specifies a pre-processor for subclass descriptions "
252             . $preprocessor . " which is not defined in the "
253             . $self->class_name . " package!";
254             }
255 29         289 $current_desc = $self->class_name->$preprocessor($current_desc);
256 29         833 $current_desc = $self->_normalize_class_description_impl(%$current_desc);
257             }
258              
259             # only call it on the direct parent classes, let recursion walk the tree
260             my @parent_class_names =
261 38907         78911 grep { $_->can('__meta__') }
  24604         70141  
262             $self->parent_class_names();
263              
264 38907         147847 for my $parent_class_name (@parent_class_names) {
265 24604         41839 my $parent_class = $parent_class_name->__meta__;
266 24604         44796 $current_desc = $parent_class->_preprocess_subclass_description($current_desc);
267             }
268              
269 38907         50030 return $current_desc;
270             }
271              
272             sub _construction_params_for_desc {
273 24695     24697   24679 my $class = shift;
274 24695         22311 my $desc = shift;
275              
276 24695         26579 my $class_name = $desc->{class_name};
277 24695         25418 my $meta_class_name = $desc->{meta_class_name};
278 24695         21333 my @extended_metadata;
279 24695 100       44195 if ($desc->{type_has}) {
280 118         265 @extended_metadata = ( has => [ @{ $desc->{type_has} } ] );
  118         438  
281             }
282              
283 24695 100       43581 if (
284             $meta_class_name eq __PACKAGE__
285             ) {
286 12858 50       25409 if (@extended_metadata) {
287 0         0 die "Cannot extend class metadata of $class_name because it is a class involved in UR bootstrapping.";
288             }
289 12858         27179 return();
290             }
291             else {
292 11837 100       20168 if ($bootstrapping) {
293             return (
294 5584         18034 class_name => $meta_class_name,
295             is => __PACKAGE__,
296             @extended_metadata,
297             );
298             }
299             else {
300 6253         9962 my $parent_classes = $desc->{is};
301 6253         11428 my @meta_parent_classes = map { $_ . '::Type' } @$parent_classes;
  7036         26732  
302 6253         13645 for (@$parent_classes) {
303 7036         27139 __PACKAGE__->use_module_with_namespace_constraints($_);
304 7036         8721 eval {$_->class};
  7036         38007  
305 7036 50       18364 if ($@) {
306 0         0 die "Error with parent class $_ when defining $class_name! $@";
307             }
308             }
309             return (
310 6253         29621 class_name => $meta_class_name,
311             is => \@meta_parent_classes,
312             @extended_metadata,
313             );
314             }
315             }
316              
317             }
318              
319              
320              
321             sub initialize_bootstrap_classes
322             {
323             # This is called once at the end of compiling the UR module set to handle
324             # classes which did incomplete initialization while bootstrapping.
325             # Until bootstrapping occurs is done,
326 266     273 1 508 my $class = shift;
327              
328 266         903 for my $class_meta (@partially_defined_classes) {
329 11966 50       34469 unless ($class_meta->_inform_all_parent_classes_of_newly_loaded_subclass) {
330 0         0 my $class_name = $class_meta->{class_name};
331 0         0 Carp::confess (
332             "Failed to complete inheritance linkage definition of class $class_name!"
333             . $class_meta->error_message
334             );
335             }
336              
337             }
338 266         1248 while (my $class_meta = shift @partially_defined_classes) {
339 11966 50       39535 unless ($class_meta->_complete_class_meta_object_definitions()) {
340 0         0 my $class_name = $class_meta->{class_name};
341 0         0 Carp::confess(
342             "Failed to complete definition of class $class_name!"
343             . $class_meta->error_message
344             );
345             }
346             }
347 266         609 $bootstrapping = 0;
348              
349             # It should be safe to set up callbacks now. register_callback() instead
350             # of create() so a subsequent rollback won't remove the observer.
351 266         2711 UR::Observer->register_callback(
352             subject_class_name => 'UR::Object::Property',
353             subject_id => '',
354             aspect => '',
355             priority => 1,
356             note => '',
357             once => 0,
358             callback => \&UR::Object::Type::_property_change_callback,
359             );
360             }
361              
362             sub _normalize_class_description {
363 24739     24762   35260 my $class = shift;
364 24739         64509 my $desc = $class->_normalize_class_description_impl(@_);
365              
366 24729 100       63778 $class->compose_roles($desc) unless $bootstrapping;
367              
368 24704 100       41569 unless ($bootstrapping) {
369 12738         14220 for my $parent_class_name (@{ $desc->{is} }) {
  12738         24487  
370 14304         35249 my $parent_class = $parent_class_name->__meta__;
371 14304         53206 $desc = $parent_class->_preprocess_subclass_description($desc);
372             }
373             }
374              
375             # we previously handled property meta extensions when normalizing the property
376             # now we merely save unrecognized things
377             # this is now done afterward so that parent classes can preprocess their subclasses descriptions before extending
378             # normalize the data behind the property descriptions
379 24703         23059 my @property_names = keys %{$desc->{has}};
  24703         59577  
380 24703         34757 for my $property_name (@property_names) {
381 95415 100       127062 Carp::croak("Invalid property name in class ".$desc->{class_name}.": '$property_name'")
382             unless UR::Util::is_valid_property_name($property_name);
383              
384 95410         86891 my $pdesc = $desc->{has}->{$property_name};
385 95410         69885 my $unknown_ma = delete $pdesc->{unrecognized_meta_attributes};
386 95410 100       126471 next unless $unknown_ma;
387 114         242 for my $name (keys %$unknown_ma) {
388 131 100       278 if (exists $desc->{attributes_have}->{$name}) {
389 131         210 $pdesc->{$name} = delete $unknown_ma->{$name};
390             }
391             }
392 114 50       344 if (%$unknown_ma) {
393 0         0 my $class_name = $desc->{class_name};
394 0         0 my @unknown_ma = sort keys %$unknown_ma;
395 0         0 Carp::confess("unknown meta-attributes present for $class_name $property_name: @unknown_ma\n");
396             }
397             }
398              
399 24698         36697 return $desc;
400             }
401              
402             sub _canonicalize_class_params {
403 24810     24811   28850 my($params, $mappings) = @_;
404              
405 24810         23311 my %canon_params;
406              
407 24810         41109 for my $mapping ( @$mappings ) {
408 843540         819760 my ($primary_field_name, @alternate_field_names) = @$mapping;
409 843540         680112 my @all_fields = ($primary_field_name, @alternate_field_names);
410 843540         736399 my @values = grep { defined($_) } delete @$params{@all_fields};
  1314636         1244712  
411 843540 50       1656320 if (@values > 1) {
    100          
412 0         0 Carp::confess(
413             "Multiple values in for field "
414             . join("/", @all_fields)
415             );
416             }
417             elsif (@values == 1) {
418 45786         86035 $canon_params{$primary_field_name} = $values[0];
419             }
420             }
421              
422 24810         120051 return %canon_params;
423             }
424              
425             our @CLASS_DESCRIPTION_KEY_MAPPINGS_COMMON_TO_CLASSES_AND_ROLES = (
426             [ roles => qw//],
427             [ is_abstract => qw/abstract/],
428             [ is_final => qw/final/],
429             [ is_singleton => qw//],
430             [ is_transactional => qw//],
431             [ id_by => qw/id_properties/],
432             [ has => qw/properties/],
433             [ type_has => qw//],
434             [ attributes_have => qw//],
435             [ er_role => qw/er_type/],
436             [ doc => qw/description/],
437             [ relationships => qw//],
438             [ constraints => qw/unique_constraints/],
439             [ namespace => qw//],
440             [ schema_name => qw//],
441             [ data_source_id => qw/data_source instance/],
442             [ select_hint => qw/query_hint/],
443             [ join_hint => qw//],
444             [ subclassify_by => qw/sub_classification_property_name/],
445             [ sub_classification_meta_class_name => qw//],
446             [ sub_classification_method_name => qw//],
447             [ first_sub_classification_method_name => qw//],
448             [ composite_id_separator => qw//],
449             [ generate => qw//],
450             [ generated => qw//],
451             [ subclass_description_preprocessor => qw//],
452             [ id_generator => qw/id_sequence_generator_name/],
453             [ subclassify_by_version => qw//],
454             [ meta_class_name => qw//],
455             [ valid_signals => qw//],
456             );
457              
458             my @CLASS_DESCRIPTION_KEY_MAPPINGS = (
459             @CLASS_DESCRIPTION_KEY_MAPPINGS_COMMON_TO_CLASSES_AND_ROLES,
460             [ class_name => qw//],
461             [ type_name => qw/english_name/],
462             [ is => qw/inheritance extends isa is_a/],
463             [ table_name => qw/sql dsmap/],
464             );
465              
466             sub _normalize_class_description_impl {
467 24768     24778   25889 my $class = shift;
468 24768         64122 my %old_class = @_;
469              
470 24768 50       61222 if (exists $old_class{extra}) {
471 0         0 %old_class = (%{delete $old_class{extra}}, %old_class);
  0         0  
472             }
473              
474 24768         38068 my $class_name = delete $old_class{class_name};
475              
476             my %new_class = (
477             class_name => $class_name,
478             is_singleton => $UR::Object::Type::defaults{'is_singleton'},
479             is_final => $UR::Object::Type::defaults{'is_final'},
480 24768         83330 is_abstract => $UR::Object::Type::defaults{'is_abstract'},
481             _canonicalize_class_params(\%old_class, \@CLASS_DESCRIPTION_KEY_MAPPINGS),
482             );
483              
484 24768 100       60996 if (my $pp = $new_class{subclass_description_preprocessor}) {
485 11 50       47 if (!ref($pp)) {
    0          
486 11 50       70 unless ($pp =~ /::/) {
487             # a method name, not fully qualified
488             $new_class{subclass_description_preprocessor} =
489             $new_class{class_name}
490             . '::'
491 0         0 . $new_class{subclass_description_preprocessor};
492             } else {
493 11         27 $new_class{subclass_description_preprocessor} = $pp;
494             }
495             }
496             elsif (ref($pp) ne 'CODE') {
497 0         0 die "unexpected " . ref($pp) . " reference for subclass_description_preprocessor for $class_name!";
498             }
499             }
500              
501 24768 100       47099 unless ($new_class{er_role}) {
502 24469         43313 $new_class{er_role} = $UR::Object::Type::defaults{'er_role'};
503             }
504              
505 24768         36292 my @crap = qw/source/;
506 24768         27344 delete @old_class{@crap};
507              
508 24768 100       122070 if ($class_name =~ /^(.*?)::/) {
509 23872         66040 $new_class{namespace} = $1;
510             }
511             else {
512 896         2068 $new_class{namespace} = $new_class{class_name};
513             }
514              
515 24768 100 66     89273 if (not exists $new_class{is_transactional}
516             and not $meta_classes{$class_name}
517             ) {
518 21234         45402 $new_class{is_transactional} = $UR::Object::Type::defaults{'is_transactional'};
519             }
520              
521 24768 100       47405 unless ($new_class{is}) {
522 271     268   1489 no warnings;
  268         397  
  270         11749  
523 267     280   1078 no strict 'refs';
  270         391  
  267         116755  
524 4814 100       8501 if (my @isa = @{ $class_name . "::ISA" }) {
  4814         40806  
525 538         1125 $new_class{is} = \@isa;
526             }
527             }
528              
529 24768 100       46490 unless ($new_class{is}) {
530 4276 100       9178 if ($new_class{table_name}) {
531 273         882 $new_class{is} = ['UR::Entity']
532             }
533             else {
534 4003         9780 $new_class{is} = ['UR::Object']
535             }
536             }
537              
538 24768 100       44252 unless ($new_class{'doc'}) {
539 20848         27133 $new_class{'doc'} = undef;
540             }
541              
542 24768         36678 foreach my $key ( qw(valid_signals roles) ) {
543 49536 50       109844 unless (UR::Util::ensure_arrayref(\%new_class, $key)) {
544 0         0 Carp::croak("The '$key' metadata for class $class_name must be an arrayref");
545             }
546             }
547              
548             # Later code expects these to be listrefs
549 24768         35614 for my $field (qw/is id_by has relationships constraints/) {
550 123840         134262 _massage_field_into_arrayref(\%new_class, $field);
551             }
552              
553              
554             # These may have been found and moved over. Restore.
555 24768         38053 $old_class{has} = delete $new_class{has};
556 24768         34067 $old_class{attributes_have} = delete $new_class{attributes_have};
557              
558             # Install structures to track fully formatted property data.
559 24768         36419 my $instance_properties = $new_class{has} = {};
560 24768         54004 my $meta_properties = $new_class{attributes_have} = {};
561              
562             # The id might be a single value, or not specified at all.
563 24768         22725 my $id_properties;
564 24768 50 33     130548 if (not exists $new_class{id_by}) {
    50          
565 0 0       0 if ($new_class{is}) {
566 0         0 $id_properties = $new_class{id_by} = [];
567             }
568             else {
569 0         0 $id_properties = $new_class{id_by} = [ id => { is_optional => 0 } ];
570             }
571             }
572             elsif ( (not ref($new_class{id_by})) or (ref($new_class{id_by}) ne 'ARRAY') ) {
573 0         0 $id_properties = $new_class{id_by} = [ $new_class{id_by} ];
574             }
575             else {
576 24768         28451 $id_properties = $new_class{id_by};
577             }
578              
579 24768         49637 _normalize_id_property_data(\%old_class, \%new_class);
580              
581 24768 50 100     64177 if (@$id_properties > 1
582 5200         11747 and grep {$_ eq 'id'} @$id_properties)
583             {
584             Carp::croak("Cannot initialize class $class_name: "
585             . "Cannot have an ID property named 'id' when the class has multiple ID properties ("
586 0         0 . join(', ', map { "'$_'" } @$id_properties)
  0         0  
587             . ")");
588             }
589              
590 24768         54639 _process_class_definition_property_keys(\%old_class, \%new_class);
591              
592             # NOT ENABLED YET
593 24768         23937 if (0) {
594             # done processing direct properties of this process
595             # extend %$instance_properties with properties of the parent classes
596             my @parent_class_names = @{ $new_class{is} };
597             for my $parent_class_name (@parent_class_names) {
598             my $parent_class_meta = $parent_class_name->__meta__;
599             die "no meta for $parent_class_name while initializing $class_name?" unless $parent_class_meta;
600             my $parent_normalized_properties = $parent_class_meta->{has};
601             for my $parent_property_name (keys %$parent_normalized_properties) {
602             my $parent_property_data = $parent_normalized_properties->{$parent_property_name};
603             my $inherited_copy = $instance_properties->{$parent_property_name};
604             unless ($inherited_copy) {
605             $inherited_copy = UR::Util::deep_copy($parent_property_data);
606             }
607             $inherited_copy->{class_name} = $class_name;
608             my $override = $inherited_copy->{overrides_class_names} ||= [];
609             push @$override, $parent_property_data->{class_name};
610             }
611             }
612             }
613              
614 24768 100 100     67417 if (($new_class{data_source_id} and not ref($new_class{data_source_id})) and not $new_class{schema_name}) {
      100        
615 658         1251 my $s = $new_class{data_source_id};
616 658         3451 $s =~ s/^.*::DataSource:://;
617 658         1602 $new_class{schema_name} = $s;
618             }
619              
620 24768 100       48050 if (%old_class) {
621             # this should have all been deleted above
622             # we actually process it later, since these may be related to parent classes extending
623             # the class definition
624 263         786 $new_class{extra} = \%old_class;
625             };
626              
627             # ensure parent classes are loaded
628 24768 100       45069 unless ($bootstrapping) {
629 12802 50       25831 my @base_classes = map { ref($_) ? @$_ : $_ } $new_class{is};
  12802         49634  
630 12802         20437 for my $parent_class_name (@base_classes) {
631             # ensure the parent classes are fully processed
632 267     267   1290 no warnings;
  267         425  
  267         527786  
633 14383 100       94843 unless ($parent_class_name->can("__meta__")) {
634 43         2901 __PACKAGE__->use_module_with_namespace_constraints($parent_class_name);
635 43 50       144 Carp::croak("Class $class_name cannot initialize because of errors using parent class $parent_class_name: $@") if $@;
636             }
637 14383 100       234717 unless ($parent_class_name->can("__meta__")) {
638 3 50       99 if ($ENV{'HARNESS_ACTIVE'}) {
639 3         963 Carp::confess("Class $class_name cannot initialize because of errors using parent class $parent_class_name. Failed to find static method '__meta__' on $parent_class_name. Does class $parent_class_name exist, and is it loaded?\n The entire list of base classes was ".join(', ', @base_classes));
640             }
641 0         0 Carp::croak("Class $class_name cannot initialize because of errors using parent class $parent_class_name. Failed to find static method '__meta__' on $parent_class_name. Does class $parent_class_name exist, and is it loaded?");
642             }
643 14380         80361 my $parent_class = $parent_class_name->__meta__;
644 14380 50       32829 unless ($parent_class) {
645 0         0 Carp::carp("No class metadata object for $parent_class_name");
646 0         0 next;
647             }
648              
649             # the the parent classes indicate version, if needed
650 14380 100 66     44770 if ($parent_class->{'subclassify_by_version'} and not $parent_class_name =~ /::Ghost/) {
651 39 100       537 unless ($class_name =~ /^${parent_class_name}::V\d+/) {
652 15         23 my $ns = $parent_class_name;
653 15         25 $ns =~ s/::.*//;
654 15         15 my $version;
655 15 50 66     68 if ($ns and $ns->can("component_version")) {
656 0         0 $version = $ns->component_version($class);
657             }
658 15 50       656 unless ($version) {
659 15         22 $version = '1';
660             }
661 15         29 $parent_class_name = $parent_class_name . '::V' . $version;
662 15         1789 eval "use $parent_class_name";
663 15 50       236 Carp::confess("Error using versioned module $parent_class_name!:\n$@") if $@;
664 15         40 redo;
665             }
666             }
667             }
668 12799         23802 $new_class{is} = \@base_classes;
669             }
670              
671             # allow parent classes to adjust the description in systematic ways
672 24765         24709 my @additional_property_meta_attributes;
673 24765 100       40770 unless ($bootstrapping) {
674 12799         12220 for my $parent_class_name (@{ $new_class{is} }) {
  12799         22543  
675 14365         23155 my $parent_class = $parent_class_name->__meta__;
676 14365 50       35729 if (my $parent_meta_properties = $parent_class->{attributes_have}) {
677 14365         26978 push @additional_property_meta_attributes, %$parent_meta_properties;
678             }
679             }
680             }
681              
682 24765         62389 __PACKAGE__->_normalize_property_descriptions_during_normalize_class_description(\%new_class);
683              
684 24757 100       47691 unless ($bootstrapping) {
685 12791         26128 %$meta_properties = (%$meta_properties, @additional_property_meta_attributes);
686              
687             # Inheriting from an abstract class that subclasses with a subclassify_by means that
688             # this class' property named by that subclassify_by is actually a constant equal to this
689             # class' class name
690             PARENT_CLASS:
691 12791         13967 foreach my $parent_class_name ( @{ $new_class{'is'} }) {
  12791         23063  
692 14356         30173 my $parent_class_meta = $parent_class_name->__meta__();
693 14356         396315 foreach my $ancestor_class_meta ( $parent_class_meta->all_class_metas ) {
694 37998 100       78880 if (my $subclassify_by = $ancestor_class_meta->subclassify_by) {
695 155 50       847 if (not $instance_properties->{$subclassify_by}) {
696 155         724 my %old_property = (
697             property_name => $subclassify_by,
698             default_value => $class_name,
699             is_constant => 1,
700             is_classwide => 1,
701             is_specified_in_module_header => 0,
702             column_name => '',
703             implied_by => $parent_class_meta->class_name . '::subclassify_by',
704             );
705 155         745 my %new_property = $class->_normalize_property_description1($subclassify_by, \%old_property, \%new_class);
706 155         893 my %new_property2 = $class->_normalize_property_description2(\%new_property, \%new_class);
707 155         534 $instance_properties->{$subclassify_by} = \%new_property2;
708 155         725 last PARENT_CLASS;
709             }
710             }
711             }
712             }
713             }
714              
715 24757         76931 my $meta_class_name = __PACKAGE__->_resolve_meta_class_name_for_class_name($class_name);
716 24757   100     92287 $new_class{meta_class_name} ||= $meta_class_name;
717 24757         69381 return \%new_class;
718             }
719              
720             # Transform the id properties into a list of raw ids,
721             # and move the property definitions into "id_implied"
722             # where present so they can be processed below.
723             sub _normalize_id_property_data {
724 24810     24810   26300 my($old_class_desc, $new_class_desc) = @_;
725              
726 24810         30153 my $id_properties = $new_class_desc->{id_by};
727 24810         24234 my $property_rank = 0;
728 24810         21167 my @replacement;
729 24810         31342 my $pos = 0;
730              
731 24810         60482 for(my $n = 0; $n < @$id_properties; $n++) {
732 6470         9163 my $name = $id_properties->[$n];
733              
734 6470         9810 my $data = $id_properties->[$n+1];
735 6470 100       10609 if (ref($data)) {
736 3237   66     15346 $old_class_desc->{id_implied}->{$name} ||= $data;
737 3237 100       5502 if (my $obj_ids = $data->{id_by}) {
738 301 100       1250 push @replacement, (ref($obj_ids) ? @$obj_ids : ($obj_ids));
739             }
740             else {
741 2936         3600 push @replacement, $name;
742             }
743 3237         3254 $n++;
744             }
745             else {
746 3233   66     15927 $old_class_desc->{id_implied}->{$name} ||= {};
747 3233         4941 push @replacement, $name;
748             }
749 6470         18305 $old_class_desc->{id_implied}->{$name}->{'position_in_module_header'} = $pos++;
750             }
751 24810         44516 @$id_properties = @replacement;
752             }
753              
754             # Given several different kinds of input, convert it into an arrayref
755             sub _massage_field_into_arrayref {
756 123882     123882   97593 my($class_desc, $field_name) = @_;
757              
758 123882         106337 my $value = $class_desc->{$field_name};
759 123882         98755 my $reftype = ref $value;
760 123882 100       217742 if (! exists $class_desc->{$field_name}) {
    100          
    100          
    50          
761 89944         133297 $class_desc->{$field_name} = [];
762              
763             } elsif (! $reftype) {
764             # It's a plain string, wrap it in an arrayref
765 7017         16169 $class_desc->{$field_name} = [ $value ];
766              
767             } elsif ($reftype eq 'HASH') {
768             # Later code expects it to be a listref - convert it
769 38         147 $class_desc->{$field_name} = [ %$value ];
770              
771             } elsif ($reftype ne 'ARRAY') {
772 0         0 my $class_name = $class_desc->{class_name};
773 0         0 Carp::croak "$class_name cannot initialize because its $field_name section is not a string, arrayref or hashref";
774              
775             }
776             }
777              
778             sub _normalize_property_descriptions_during_normalize_class_description {
779 37503     37503   43606 my($class, $new_class) = @_;
780              
781 37503         40986 my $instance_properties = $new_class->{has};
782              
783             # normalize the data behind the property descriptions
784 37503         81456 my @property_names = keys %$instance_properties;
785 37503         48067 for my $property_name (@property_names) {
786 142339         107709 my %old_property = %{ $instance_properties->{$property_name} };
  142339         633372  
787 142339         294214 my %new_property = $class->_normalize_property_description1($property_name, \%old_property, $new_class);
788 142335         419128 %new_property = $class->_normalize_property_description2(\%new_property, $new_class);
789 142331         629908 $instance_properties->{$property_name} = \%new_property;
790             }
791              
792             # Find 'via' properties where the to is '-filter' and rewrite them to
793             # copy some attributes from the source property
794             # This feels like a hack, but it makes other parts of the system easier by
795             # not having to deal with -filter
796 37495         47027 foreach my $property_name ( @property_names ) {
797 142330         112464 my $property_data = $instance_properties->{$property_name};
798 142330 100 100     275002 if ($property_data->{'to'} && $property_data->{'to'} eq '-filter') {
799 36         43 my $via = $property_data->{'via'};
800 36         34 my $via_property_data = $instance_properties->{$via};
801 36 50       65 unless ($via_property_data) {
802 0         0 my $class_name = $new_class->{class_name};
803 0         0 Carp::croak "Cannot initialize class $class_name: Property '$property_name' filters '$via', but there is no property '$via'.";
804             }
805              
806 36         41 $property_data->{'data_type'} = $via_property_data->{'data_type'};
807 36         40 $property_data->{'reverse_as'} = $via_property_data->{'reverse_as'};
808 36 50       67 if ($via_property_data->{'where'}) {
809 0         0 unshift @{$property_data->{'where'}}, @{$via_property_data->{'where'}};
  0         0  
  0         0  
810             }
811             }
812             }
813              
814             # Catch a mistake in the class definition where a property is 'via'
815             # something, and its 'to' is the same as the via's reverse_as. This
816             # ends up being a circular definition and generates junk SQL
817 37495         54283 foreach my $property_name ( @property_names ) {
818 142330         100127 my $property_data = $instance_properties->{$property_name};
819 142330         104875 my $via = $property_data->{'via'};
820 142330         95470 my $to = $property_data->{'to'};
821 142330 100 66     254295 if (defined($via) and defined($to)) {
822 25947         21827 my $via_property_data = $instance_properties->{$via};
823 25947 100 100     60566 next unless ($via_property_data and $via_property_data->{'reverse_as'});
824 5788 50       11875 if ($via_property_data->{'reverse_as'} eq $to) {
825 0         0 my $class_name = $new_class->{class_name};
826 0         0 Carp::croak("Cannot initialize class $class_name: Property '$property_name' defines "
827             . "an incompatible relationship. Its 'to' is the same as reverse_as for property '$via'");
828             }
829             }
830             }
831             }
832              
833             sub _process_class_definition_property_keys {
834 24810     24810   27069 my($old_class, $new_class) = @_;
835              
836 24810         52807 my($class_name, $instance_properties, $meta_properties) = @$new_class{'class_name', 'has','attributes_have'};
837 24810   100     46309 $class_name ||= $new_class->{role_name}; # This is used by role construction, too
838              
839             # Flatten and format the property list(s) in the class description.
840             # NOTE: we normalize the details at the end of normalizing the class description.
841 24810         45910 my @keys = _class_definition_property_keys_in_processing_order($old_class);
842 24810         40680 foreach my $key ( @keys ) {
843             # parse the key to see if we're looking at instance or meta attributes,
844             # and take the extra words as additional attribute meta-data.
845 55891         47191 my @added_property_meta;
846             my $properties;
847 55891 100       149477 if ($key =~ /has/) {
    100          
    50          
848             @added_property_meta =
849 27468         76341 grep { $_ ne 'has' } split(/[_-]/,$key);
  31316         57416  
850 27468         29715 $properties = $instance_properties;
851             }
852             elsif ($key =~ /attributes_have/) {
853             @added_property_meta =
854 24810 100       74528 grep { $_ ne 'attributes' and $_ ne 'have' } split(/[_-]/,$key);
  49620         147923  
855 24810         28222 $properties = $meta_properties;
856             }
857             elsif ($key eq 'id_implied') {
858             # these are additions to the regular "has" list from complex identity properties
859 3613         6582 $properties = $instance_properties;
860             }
861             else {
862 0         0 die "Odd key $key?";
863             }
864 55891         58515 @added_property_meta = map { 'is_' . $_ => 1 } @added_property_meta;
  3848         9845  
865              
866             # the property data can be a string, array, or hash as they come in
867             # convert string, hash and () into an array
868 55891         67606 my $property_data = delete $old_class->{$key};
869              
870 55891         44172 my @tmp;
871 55891 100       128805 if (!ref($property_data)) {
    100          
    50          
872 24449 50       35629 if (defined($property_data)) {
873 0         0 @tmp = split(/\s+/, $property_data);
874             }
875             else {
876 24449         26167 @tmp = ();
877             }
878             }
879             elsif (ref($property_data) eq 'HASH') {
880             @tmp = map {
881 3978         15144 ($_ => $property_data->{$_})
  6635         14768  
882             } sort keys %$property_data;
883             }
884             elsif (ref($property_data) eq 'ARRAY') {
885 27464         50449 @tmp = @$property_data;
886             }
887             else {
888 0         0 die "Unrecognized data $property_data appearing as property list!";
889             }
890              
891             # process the array of property specs
892 55891         49427 my $pos = 0;
893 55891         101615 while (my $name = shift @tmp) {
894 101775         62198 my $params;
895 101775 100       105730 if (ref($tmp[0])) {
896 100903         67593 $params = shift @tmp;
897 100903 50       132967 unless (ref($params) eq 'HASH') {
898 0         0 my $seen_type = ref($params);
899 0         0 Carp::confess("class $class_name property $name has a $seen_type reference instead of a hashref describing its meta-attributes!");
900             }
901 100903 100       167032 %$params = (@added_property_meta, %$params) if @added_property_meta;
902             }
903             else {
904 872         1458 $params = { @added_property_meta };
905             }
906              
907 101775 100       138235 unless (exists $params->{'position_in_module_header'}) {
908 79744         65189 $params->{'position_in_module_header'} = $pos++;
909             }
910 101775 100       123069 unless (exists $params->{is_specified_in_module_header}) {
911 80537         116846 $params->{is_specified_in_module_header} = $class_name . '::' . $key;
912             }
913              
914             # Indirect properties can mention the same property name more than once. To
915             # avoid stomping over existing property data with this other property data,
916             # merge the new info into the existing hash. Otherwise, the new property name
917             # gets an empty hash of info
918 101775 100       109542 if ($properties->{$name}) {
919             # this property already exists, but is also implied by some other property which added it to the end of the listed
920             # extend the existing definition
921 6131         14824 foreach my $key ( keys %$params ) {
922 32890 100 100     81934 next if ($key eq 'is_specified_in_module_header' || $key eq 'position_in_module_header');
923             # once a property gets set to is_optional => 0, it stays there, even if it's later set to 1
924             next if ($key eq 'is_optional'
925             and
926             exists($properties->{$name}->{'is_optional'})
927             and
928             defined($properties->{$name}->{'is_optional'})
929             and
930 20628 100 100     34006 $properties->{$name}->{'is_optional'} == 0);
      100        
      100        
931 20498         22978 $properties->{$name}->{$key} = $params->{$key};
932             }
933 6131         8234 $params = $properties->{$name};
934             } else {
935 95644         111918 $properties->{$name} = $params;
936             }
937              
938             # a single calculate_from can be a simple string, convert to a listref
939 101775 100       135131 if (my $calculate_from = $params->{'calculate_from'}) {
940 669 100       2349 $params->{'calculate_from'} = [ $calculate_from ] unless (ref($calculate_from) eq 'ARRAY');
941             }
942              
943 101775 100       124885 if (my $id_by = $params->{id_by}) {
944 4690 100       12742 $id_by = [ $id_by ] unless ref($id_by) eq 'ARRAY';
945 4690         6702 my @id_by_names;
946 4690         11139 while (@$id_by) {
947 5131         6164 my $id_name = shift @$id_by;
948 5131         5084 my $params2;
949 5131 50       8707 if (ref($id_by->[0])) {
950 0         0 $params2 = shift @$id_by;
951             }
952             else {
953 5131         5881 $params2 = {};
954             }
955 5131         8480 for my $p (@UR::Object::Type::meta_id_ref_shared_properties) {
956 61572 100       83208 if (exists $params->{$p}) {
957 8368         10324 $params2->{$p} = $params->{$p};
958             }
959             }
960 5131         7608 $params2->{implied_by} = $name;
961 5131         6042 $params2->{is_specified_in_module_header} = 0;
962              
963 5131         6905 push @id_by_names, $id_name;
964 5131         10832 push @tmp, $id_name, $params2;
965             }
966 4690         6661 $params->{id_by} = \@id_by_names;
967             }
968              
969 101775 100       212327 if (my $id_class_by = $params->{'id_class_by'}) {
970 546 50       1392 if (ref $id_class_by) {
971 0         0 Carp::croak("Cannot initialize class $class_name: "
972             . "Property $name has an 'id_class_by' that is not a plain string");
973             }
974 546         2560 push @tmp, $id_class_by, { implied_by => $name, is_specified_in_module_header => 0 };
975             }
976              
977             } # next property in group
978              
979             # id-by properties' metadata can influence the id-ed-by property metadata
980 55891         130093 for my $pdata (values %$properties) {
981 116162 100       155412 next unless $pdata->{id_by};
982 6079         5848 for my $id_property (@{ $pdata->{id_by} }) {
  6079         9558  
983 7382         7772 my $id_pdata = $properties->{$id_property};
984 7382         8154 for my $p (@UR::Object::Type::meta_id_ref_shared_properties) {
985 88584 100 100     424312 if (exists $id_pdata->{$p} xor exists $pdata->{$p}) {
    100 66        
      100        
986             # if one or the other specifies a value, copy it to the one that's missing
987 6918   66     17001 $id_pdata->{$p} = $pdata->{$p} = $id_pdata->{$p} || $pdata->{$p};
988             } elsif (!exists $id_pdata->{$p} and !exists $pdata->{$p} and exists $UR::Object::Property::defaults{$p}) {
989             # if neither has a value, use the default for both
990 31593         52238 $id_pdata->{$p} = $pdata->{$p} = $UR::Object::Property::defaults{$p};
991             }
992             }
993             }
994             }
995              
996             }
997             }
998              
999             sub compose_roles {
1000 12763     12763 1 18596 my($class, $desc) = @_;
1001              
1002 12763         64888 UR::Role::Prototype->_apply_roles_to_class_desc($desc);
1003 12738         27919 $class->_normalize_property_descriptions_during_normalize_class_description($desc);
1004             }
1005              
1006             # Return the order to process the has, has_optional, has_constant, etc keys
1007             sub _class_definition_property_keys_in_processing_order {
1008 24810     24810   27289 my $class_hashref = shift;
1009              
1010 24810         23261 my @order;
1011              
1012             # we want to hit 'id_implied' first to preserve position_ and is_specified_ keys
1013 24810 100       52296 push(@order, 'id_implied') if exists $class_hashref->{id_implied};
1014              
1015             # 'has' next so is_optional can get set to 0 in case the same property also appears in has_optional
1016 24810 50       56762 push(@order, 'has') if exists $class_hashref->{has};
1017              
1018             # everything else
1019 24810         50105 push @order, grep { /has_|attributes_have/ } keys %$class_hashref;
  56155         201844  
1020              
1021 24810         57591 return @order;
1022             }
1023              
1024              
1025              
1026             sub _normalize_property_description1 {
1027 142511     142511   124031 my $class = shift;
1028 142511         103322 my $property_name = shift;
1029 142511         102309 my $property_data = shift;
1030 142511   66     206529 my $class_data = shift || $class;
1031 142511         123699 my $class_name = $class_data->{class_name};
1032 142511         442169 my %old_property = %$property_data;
1033 142511         616655 my %new_class = %$class_data;
1034              
1035 142511 100       290981 if (exists $old_property{unrecognized_meta_attributes}) {
1036 113         135 %old_property = (%{delete $old_property{unrecognized_meta_attributes}}, %old_property);
  113         941  
1037             }
1038              
1039 142511         124347 delete $old_property{source};
1040              
1041 142511 50 100     252579 if ($old_property{implied_by} and $old_property{implied_by} eq $property_name) {
1042 0         0 $class->warning_message("Cleaning up odd self-referential 'implied_by' on $class_name $property_name");
1043 0         0 delete $old_property{implied_by};
1044             }
1045              
1046             # Only 1 of is_abstract, is_concrete or is_final may be set
1047             {
1048 267     267   1530 no warnings 'uninitialized';
  267         440  
  267         392982  
  142511         104154  
1049             my $modifier_sum = $old_property{is_abstract}
1050             + $old_property{is_concrete}
1051 142511         199454 + $old_property{is_final};
1052              
1053 142511 50       312701 if ($modifier_sum > 1) {
    100          
1054 0         0 Carp::confess("abstract/concrete/final are mutually exclusive. Error in class definition for $class_name property $property_name!");
1055             } elsif ($modifier_sum == 0) {
1056 79036         81846 $old_property{is_concrete} = 1;
1057             }
1058             }
1059              
1060 142511         216913 my %new_property = (
1061             class_name => $class_name,
1062             property_name => $property_name,
1063             );
1064              
1065 142511         1892567 for my $mapping (
1066             [ property_type => qw/resolution/],
1067             [ class_name => qw//],
1068             [ property_name => qw//],
1069             [ column_name => qw/sql/],
1070             [ constraint_name => qw//],
1071             [ data_length => qw/len/],
1072             [ data_type => qw/type is isa is_a/],
1073             [ calculated_default => qw//],
1074             [ default_value => qw/default value/],
1075             [ valid_values => qw//],
1076             [ example_values => qw//],
1077             [ doc => qw/description/],
1078             [ is_optional => qw/is_nullable nullable optional/],
1079             [ is_transient => qw//],
1080             [ is_volatile => qw//],
1081             [ is_constant => qw//],
1082             [ is_classwide => qw/is_class_wide/],
1083             [ is_delegated => qw//],
1084             [ is_calculated => qw//],
1085             [ is_mutable => qw//],
1086             [ is_transactional => qw//],
1087             [ is_abstract => qw//],
1088             [ is_concrete => qw//],
1089             [ is_final => qw//],
1090             [ is_many => qw//],
1091             [ is_deprecated => qw//],
1092             [ is_undocumented => qw//],
1093             [ is_numeric => qw//],
1094             [ is_id => qw//],
1095             [ id_by => qw//],
1096             [ id_class_by => qw//],
1097             [ specify_by => qw//],
1098             [ order_by => qw//],
1099             [ access_as => qw//],
1100             [ via => qw//],
1101             [ to => qw//],
1102             [ where => qw/restrict filter/],
1103             [ implied_by => qw//],
1104             [ calculate => qw//],
1105             [ calculate_from => qw//],
1106             [ calculate_perl => qw/calc_perl/],
1107             [ calculate_sql => qw/calc_sql/],
1108             [ calculate_js => qw//],
1109             [ reverse_as => qw/reverse_id_by im_its/],
1110             [ is_legacy_eav => qw//],
1111             [ is_dimension => qw//],
1112             [ is_specified_in_module_header => qw//],
1113             [ position_in_module_header => qw//],
1114             [ singular_name => qw//],
1115             [ plural_name => qw//],
1116             ) {
1117 7125507         4568369 my $primary_field_name = $mapping->[0];
1118              
1119 7125507         3859064 my $found_key;
1120 7125507         4772131 foreach my $key ( @$mapping ) {
1121 9975712 100       12065808 if (exists $old_property{$key}) {
1122 1826068 100       1925409 if ($found_key) {
1123 1         2 my @keys = grep { exists $old_property{$_} } @$mapping;
  5         6  
1124 1         27 Carp::croak("Invalid class definition for $class_name in property '$property_name'. The keys "
1125             . join(', ',$found_key,@keys) . " are all synonyms for $primary_field_name");
1126             }
1127 1826067         1343520 $found_key = $key;
1128             }
1129             }
1130              
1131 7125506 100       9708783 if ($found_key) {
    100          
1132 1826066         2104046 $new_property{$primary_field_name} = delete $old_property{$found_key};
1133             } elsif (exists $UR::Object::Property::defaults{$primary_field_name}) {
1134 896935         949613 $new_property{$primary_field_name} = $UR::Object::Property::defaults{$primary_field_name};
1135             }
1136             }
1137              
1138 142510 50       779753 if (my $data = delete $old_property{delegate}) {
1139 0 0 0     0 if ($data->{via} =~ /^eav_/ and $data->{to} eq 'value') {
1140 0         0 $new_property{is_legacy_eav} = 1;
1141             }
1142             else {
1143 0         0 die "Odd delegation for $property_name: "
1144             . Data::Dumper::Dumper($data);
1145             }
1146             }
1147              
1148 142510 100 66     216333 if ($new_property{default_value} && $new_property{calculated_default}) {
1149 1         17 die qq(Can't initialize class $class_name: Property '$new_property{property_name}' has both default_value and calculated_default specified.);
1150             }
1151              
1152 142509 100       189317 if ($new_property{calculated_default}) {
1153 18 100       44 if ($new_property{calculated_default} eq 1) {
1154 4         10 $new_property{calculated_default} = '__default_' . $new_property{property_name} . '__';
1155             }
1156              
1157 18         25 my $ref = ref $new_property{calculated_default};
1158 18 50 66     57 if ($ref and $ref ne 'CODE') {
1159 0         0 die qq(Can't initialize class $class_name: Property '$new_property{property_name}' has calculated_default specified as a $ref ref but it must be a method name or coderef.);
1160             }
1161              
1162 18 100       26 unless ($ref) {
1163 6         30 my $method = $class_name->can($new_property{calculated_default});
1164 6 100       262 unless ($method) {
1165 2         26 die qq(Can't initialize class $class_name: Property '$new_property{property_name}' has calculated_default specified as '$new_property{calculated_default}' but method does not exist.);
1166             }
1167 4         6 $new_property{calculated_default} = $method;
1168             }
1169             }
1170              
1171 142507 50 66     208686 if ($new_property{id_by} && $new_property{reverse_as}) {
1172 0         0 die qq(Can't initialize class $class_name: Property '$new_property{property_name}' has both id_by and reverse_as specified.);
1173             }
1174              
1175 142507 100       196419 if ($new_property{data_type}) {
1176 85778 50       197084 if (my (undef, $length) = $new_property{data_type} =~ m/(\s*)\((\d+)\)$/) {
1177 0         0 $new_property{data_length} = $length;
1178             }
1179 85778 50 33     226731 if ($new_property{data_type} =~ m/[^\w:]/
      66        
1180             and
1181             (!ref($new_property{data_type}) or !$new_property{data_type}->isa('UR::Role::Param'))
1182             ) {
1183             Carp::croak("Can't initialize class $class_name: Property '" . $new_property{property_name}
1184 0         0 . "' has metadata for is/data_type that does not look like a class name ($new_property{data_type})");
1185             }
1186             }
1187              
1188 142507 100       195091 if (%old_property) {
1189 227         313 $new_property{unrecognized_meta_attributes} = \%old_property;
1190 227         1896 %new_property = (%old_property, %new_property);
1191             }
1192              
1193 142507         1400974 return %new_property;
1194             }
1195              
1196             sub _normalize_property_description2 {
1197 142490     142490   127802 my $class = shift;
1198 142490         101233 my $property_data = shift;
1199 142490   33     209312 my $class_data = shift || $class;
1200              
1201 142490         129943 my $property_name = $property_data->{property_name};
1202 142490         111403 my $class_name = $property_data->{class_name};
1203              
1204 142490         686413 my %new_property = %$property_data;
1205 142490         697635 my %new_class = %$class_data;
1206              
1207 142490 100 100     419563 if (grep { $_ ne 'is_calculated' && $_ ne 'calculated_default' && /calc/ } keys %new_property) {
  2913813 100       9908530  
1208 3700         5045 $new_property{is_calculated} = 1;
1209             }
1210              
1211 142490 100 66     587129 if ($new_property{via}
      33        
      66        
1212             || $new_property{to}
1213             || $new_property{id_by}
1214             || $new_property{reverse_as}
1215             ) {
1216 36707         35690 $new_property{is_delegated} = 1;
1217 36707 100 100     111448 if (defined $new_property{via} and not defined $new_property{to}) {
1218 1902         2355 $new_property{to} = $property_name;
1219             }
1220             }
1221              
1222 142490 100       209801 if (!defined($new_property{is_mutable})) {
1223 75280 100 100     208769 if ($new_property{is_delegated}
      66        
1224             or
1225             (defined $class_data->{'subclassify_by'} and $class_data->{'subclassify_by'} eq $property_name)
1226             ) {
1227 16565         15265 $new_property{is_mutable} = 0;
1228             }
1229             else {
1230 58715         51393 $new_property{is_mutable} = 1;
1231             }
1232             }
1233              
1234             # For classes that have (or pretend to have) tables, the Property objects
1235             # should get their column_name property automatically filled in
1236 142490         103294 my $the_data_source;
1237 142490 100       282247 if (ref($new_class{'data_source_id'}) eq 'HASH') {
    100          
1238             # This is an inline-defined data source
1239 96         131 $the_data_source = $new_class{'data_source_id'}->{'is'};
1240             } elsif ($new_class{'data_source_id'}) {
1241 5785         6674 $the_data_source = $new_class{'data_source_id'};
1242             # using local() here to save $@ doesn't work. You end up with the
1243             # error "Unknown error" if one of the parent classes of the data source has
1244             # some kind of problem
1245 5785         5843 my $dollarat = $@;
1246 5785         5303 $@ = '';
1247 5785   100     19547 $the_data_source = UR::DataSource->get($the_data_source) || eval { $the_data_source->get() };
1248 5785 100       10533 unless ($the_data_source) {
1249             my $error = "Can't resolve data source from value '"
1250 4         14 . $new_class{'data_source_id'}
1251             . "' in class definition for $class_name";
1252 4 50       8 if ($@) {
1253 4         9 $error .= "\n$@";
1254             }
1255 4         741 Carp::croak($error);
1256             }
1257 5781         6397 $@ = $dollarat;
1258             }
1259             # UR::DataSource::File-backed classes don't have table_names, but for querying/saving to
1260             # work property, their properties still have to have column_name filled in
1261 142486 100 66     372767 if (($new_class{table_name} or ($the_data_source and ($the_data_source->initializer_should_create_column_name_for_class_properties())))
      100        
      100        
      100        
      100        
      66        
1262             and not exists($new_property{column_name}) # They didn't supply a column_name
1263             and not $new_property{is_transient}
1264             and not $new_property{is_delegated}
1265             and not $new_property{is_calculated}
1266             and not $new_property{is_legacy_eav}
1267             ) {
1268 920         1392 $new_property{column_name} = $new_property{property_name};
1269 920 50 66     4288 if ($the_data_source and $the_data_source->table_and_column_names_are_upper_case) {
1270 0         0 $new_property{column_name} = uc($new_property{column_name});
1271             }
1272             }
1273              
1274 142486 50 66     232063 if ($new_property{order_by} and not $new_property{is_many}) {
1275 0         0 die "Cannot use order_by except on is_many properties!";
1276             }
1277              
1278 142486 50 66     228320 if ($new_property{specify_by} and not $new_property{is_many}) {
1279 0         0 die "Cannot use specify_by except on is_many properties!";
1280             }
1281              
1282 142486 50 66     246867 if ($new_property{implied_by} and $new_property{implied_by} eq $property_name) {
1283 0         0 $class->warnings_message("New data has odd self-referential 'implied_by' on $class_name $property_name!");
1284 0         0 delete $new_property{implied_by};
1285             }
1286              
1287 142486         1412612 return %new_property;
1288             }
1289              
1290              
1291             sub _make_minimal_class_from_normalized_class_description {
1292 24690     24690   25456 my $class = shift;
1293 24690         24558 my $desc = shift;
1294              
1295 24690         30801 my $class_name = $desc->{class_name};
1296 24690 50       42094 unless ($class_name) {
1297 0         0 Carp::confess("No class name specified?");
1298             }
1299              
1300 24690         27719 my $meta_class_name = $desc->{meta_class_name};
1301 24690 50       39088 die unless $meta_class_name;
1302 24690 100       47251 if ($meta_class_name ne __PACKAGE__) {
1303 11834 50       64041 unless (
1304             $meta_class_name->isa(__PACKAGE__)
1305             ) {
1306 0         0 warn "Bogus meta class $meta_class_name doesn't inherit from UR::Object::Type?"
1307             }
1308             }
1309              
1310             # only do this when the classes match
1311             # when they do not match, the super-class has already called this by delegating to the correct subclass
1312 24690         27086 $class_name::VERSION = 2.0; # No BumpVersion
1313              
1314 24690         237816 my $self = bless { id => $class_name, %$desc }, $meta_class_name;
1315              
1316 24690         61439 $UR::Context::all_objects_loaded->{$meta_class_name}{$class_name} = $self;
1317 24690         52593 my $full_name = join( '::', $class_name, '__meta__' );
1318             Sub::Install::reinstall_sub({
1319             into => $class_name,
1320             as => '__meta__',
1321 419401     419401   661073 code => Sub::Name::subname $full_name => sub {$self},
        1443549      
        1417140      
1322 24690         330710 });
1323              
1324 24690         1221978 return $self;
1325             }
1326              
1327             sub _initialize_accessors_and_inheritance {
1328 24690     24690   27004 my $self = shift;
1329              
1330 24690         105788 $self->initialize_direct_accessors;
1331              
1332 24690         31225 my $class_name = $self->{class_name};
1333              
1334 24690         22915 my @is = @{ $self->{is} };
  24690         46676  
1335 24690 100       47695 unless (@is) {
1336 266         755 @is = ('UR::ModuleBase')
1337             }
1338 24690         54580 eval "\@${class_name}::ISA = (" . join(',', map { "'$_'" } @is) . ")\n";
  26256         1507840  
1339 24690 50       96344 Carp::croak("Can't initialize \@ISA for class_name '$class_name': $@\nMaybe the class_name or one of the parent classes are not valid class names") if $@;
1340              
1341 24690         24831 my $namespace_mro;
1342 24690         39378 my $namespace_name = $self->{namespace};
1343 24690 50 100     298121 if (
      66        
      100        
      66        
      66        
1344             !$bootstrapping
1345             && !$class_name->isa('UR::Namespace')
1346             && $namespace_name
1347             && $namespace_name->isa('UR::Namespace')
1348             && $namespace_name->can('get')
1349             && (my $namespace = $namespace_name->get())
1350             ) {
1351 11210         35462 $namespace_mro = $namespace->method_resolution_order;
1352             }
1353              
1354 24690 0 33     335358 if ($^V lt v5.9.5 && $namespace_mro && $namespace_mro eq 'c3') {
      33        
1355 0         0 warn "C3 method resolution order is not supported on Perl < 5.9.5. Reverting $namespace_name namespace to DFS.";
1356 0         0 my $namespace = $namespace_name->get();
1357 0         0 $namespace_mro = $namespace->method_resolution_order('dfs');
1358             }
1359              
1360 24690 100 66     148513 if ($^V ge v5.9.5 && $namespace_mro && mro::get_mro($class_name) ne $namespace_mro) {
      100        
1361 11202         161289 mro::set_mro($class_name, $namespace_mro);
1362             }
1363              
1364 24690         75298 return $self;
1365             }
1366              
1367             our %_init_subclasses_loaded;
1368             sub subclasses_loaded {
1369 170082     170082 1 126018 return @{ $_init_subclasses_loaded{shift->class_name}};
  170082         383904  
1370             }
1371              
1372             our %_inform_all_parent_classes_of_newly_loaded_subclass;
1373             sub _inform_all_parent_classes_of_newly_loaded_subclass {
1374 24690     24690   27570 my $self = shift;
1375 24690         75741 my $class_name = $self->class_name;
1376              
1377 24690 50       53005 Carp::confess("re-initializing class $class_name") if $_inform_all_parent_classes_of_newly_loaded_subclass{$class_name};
1378 24690         37746 $_inform_all_parent_classes_of_newly_loaded_subclass{$class_name} = 1;
1379              
1380 268     271   1467 no strict 'refs';
  268         432  
  271         7273  
1381 268     270   1007 no warnings;
  267         397  
  267         97941  
1382 24690         21817 my @parent_classes = @{ $class_name . "::ISA" };
  24690         94152  
1383 24690         32060 for my $parent_class (@parent_classes) {
1384 26256 100       78368 unless ($parent_class->can("id")) {
1385 266         11583 __PACKAGE__->use_module_with_namespace_constraints($parent_class);
1386 266 50       808 if ($@) {
1387 0         0 die "Failed to find parent_class $parent_class for $class_name!";
1388             }
1389             }
1390             }
1391              
1392 24690         496847 my @i = sort $class_name->inheritance;
1393 24690   100     96640 $_init_subclasses_loaded{$class_name} ||= [];
1394 24690         26906 my $last_parent_class = "";
1395 24690         29677 for my $parent_class (@i) {
1396 88996 100       116881 next if $parent_class eq $last_parent_class;
1397              
1398 81014         57521 $last_parent_class = $parent_class;
1399 81014   100     122873 $_init_subclasses_loaded{$parent_class} ||= [];
1400 81014         53397 push @{ $_init_subclasses_loaded{$parent_class} }, $class_name;
  81014         104288  
1401 81014         54154 push @{ $parent_class . "::_init_subclasses_loaded" }, $class_name;
  81014         160711  
1402              
1403             # any index on a parent class must move to the child class
1404             # if the child class were loaded before the index is made, it is pushed down at index creation time
1405 81014 100       142731 if (my $parent_index_hashrefs = $UR::Object::Index::all_by_class_name_and_property_name{$parent_class}) {
1406             #print "PUSHING INDEXES FOR $parent_class to $class_name\n";
1407 13554         28941 for my $parent_property (keys %$parent_index_hashrefs) {
1408 8740         7457 my $parent_indexes = $parent_index_hashrefs->{$parent_property};
1409 8740   100     19442 my $indexes = $UR::Object::Index::all_by_class_name_and_property_name{$class_name}{$parent_property} ||= [];
1410 8740         11352 push @$indexes, @$parent_indexes;
1411             }
1412             }
1413             }
1414              
1415 24690         62803 return 1;
1416             }
1417              
1418             sub _inform_roles_of_new_class {
1419 24686     24686   29687 my $self = shift;
1420              
1421 24686         23764 foreach my $role_obj ( @{ $self->{roles} } ) {
  24686         58034  
1422 47         640 my $package = $role_obj->role_name;
1423 47 100       238 next unless my $import = $package->can('__import__');
1424 2         42 $import->($package, $self);
1425             }
1426             }
1427              
1428             sub _complete_class_meta_object_definitions {
1429 24688     24688   29883 my $self = shift;
1430              
1431             # track related objects
1432 24688         24011 my @subordinate_objects;
1433              
1434             # grab some data from the object
1435 24688         37898 my $class_name = $self->{class_name};
1436 24688         30062 my $table_name = $self->{table_name};
1437              
1438             # decompose the embedded complex data structures into normalized objects
1439 24688         31676 my $inheritance = $self->{is};
1440 24688         28983 my $properties = $self->{has};
1441 24688   50     51671 my $relationships = $self->{relationships} || [];
1442 24688         29429 my $constraints = $self->{constraints};
1443 24688         27884 my $data_source = $self->{'data_source_id'};
1444              
1445 24688         28339 my $id_properties = $self->{id_by};
1446 24688         23431 my %id_property_rank;
1447 24688         97676 for (my $i = '0 but true'; $i < @$id_properties; $i++) {
1448 6895         21330 $id_property_rank{$id_properties->[$i]} = $i;
1449             }
1450              
1451             # mark id/non-id properites
1452 24688         55608 foreach my $pinfo ( values %$properties ) {
1453 95383         143254 $pinfo->{'is_id'} = $id_property_rank{$pinfo->{'property_name'}};
1454             }
1455              
1456             # handle inheritance
1457 24688 100       51936 unless ($class_name eq "UR::Object") {
1458 267     270   1377 no strict 'refs';
  267         443  
  268         247858  
1459              
1460             # sanity check
1461 24422         46247 my @expected = @$inheritance;
1462 24422         22844 my @actual = @{ $class_name . "::ISA" };
  24422         89032  
1463              
1464 24422 50 33     130899 if (@actual and "@actual" ne "@expected") {
1465 0         0 Carp::confess("for $class_name: expected '@expected' actual '@actual'\n");
1466             }
1467              
1468             # set
1469 24422         31939 @{ $class_name . "::ISA" } = @$inheritance;
  24422         421062  
1470             }
1471              
1472 24688 100 100     204109 if (not $data_source and $class_name->can("__load__")) {
1473             # $data_source = UR::DataSource::Default->__define__;
1474 1334         15339 $data_source = $self->{data_source_id} = $self->{db_committed}->{data_source_id} = 'UR::DataSource::Default';
1475             }
1476              
1477             # Create inline data source
1478 24688 100 100     1256619 if ($data_source and ref($data_source) eq 'HASH') {
1479 11         28 $self->{'__inline_data_source_data'} = $data_source;
1480 11         22 my $ds_class = $data_source->{'is'};
1481 11         55 my $inline_ds = $ds_class->create_from_inline_class_data($self, $data_source);
1482 10         27 $self->{'data_source_id'} = $self->{'db_committed'}->{'data_source_id'} = $inline_ds->id;
1483             }
1484              
1485            
1486 24687 100 100     70048 if ($self->{'data_source_id'} and !defined($self->{table_name})) {
1487 1612   66     8383 my $data_source_obj = UR::DataSource->get($self->{'data_source_id'}) || eval { $self->{'data_source_id'}->get() };
1488 1612 100 66     10354 if ($data_source_obj and $data_source_obj->initializer_should_create_column_name_for_class_properties() ) {
1489 17         40 $self->{table_name} = '__default__';
1490             }
1491             }
1492              
1493 24687         42482 for my $parent_class_name (@$inheritance) {
1494 25987         60550 my $parent_class = $parent_class_name->__meta__;
1495 25987 50       52969 unless ($parent_class) {
1496             #$DB::single = 1;
1497 0         0 $parent_class = $parent_class_name->__meta__;
1498 0         0 $self->error_message("Failed to find parent class $parent_class_name\n");
1499 0         0 return;
1500             }
1501              
1502             # These class meta values get propogated from parent to child
1503 25987         36813 foreach my $inh_property ( qw(schema_name data_source_id) ) {
1504 51974 100       192897 if (not defined ($self->$inh_property)) {
1505 49173 100       91040 if (my $inh_value = $parent_class->$inh_property) {
1506 1386         4335 $self->{$inh_property} = $self->{'db_committed'}->{$inh_property} = $inh_value;
1507             }
1508             }
1509             }
1510              
1511             # For classes with no data source, the default for id_generator is -urinternal
1512             # For classes with a data source, autogenerate_new_object_id_for_class_name_and_rule gets called
1513             # on that data source which can use id_generator as it sees fit
1514 25987 100       55337 if (! defined $self->{id_generator}) {
1515 24390         23871 my $id_generator;
1516 24390 100       40970 if ($self->{data_source_id}) {
1517 2076 100 66     6470 if ($parent_class->data_source_id
1518             and
1519             $parent_class->data_source_id eq $self->data_source_id
1520             ) {
1521 1517         6628 $id_generator = $parent_class->id_generator;
1522             }
1523             } else {
1524 22314         66462 $id_generator = $parent_class->id_generator;
1525             }
1526 24390         60152 $self->{id_generator} = $self->{'db_committed'}->{id_generator} = $id_generator;
1527             }
1528              
1529              
1530             # If a parent is declared as a singleton, we are too.
1531             # This only works for abstract singletons.
1532 25987 50 33     72220 if ($parent_class->is_singleton and not $self->is_singleton) {
1533 0         0 $self->is_singleton($parent_class->is_singleton);
1534             }
1535             }
1536              
1537             # when we "have" an object reference, add it to the list of old-style references
1538             # also ensure the old-style property definition is complete
1539 24687         52223 for my $pinfo (grep { $_->{id_by} } values %$properties) {
  95381         83099  
1540 4683         9847 push @$relationships, $pinfo->{property_name}, $pinfo;
1541              
1542 4683         6568 my $id_properties = $pinfo->{id_by};
1543 4683         6824 my $r_class_name = $pinfo->{data_type};
1544 4683 50       9001 unless($r_class_name) {
1545             die sprintf("Object accessor property definition for %s::%s has an 'id_by' but no 'data_type'",
1546 0         0 $pinfo->{'class_name'}, $pinfo->{'property_name'});
1547             }
1548 4683         4708 my $r_class;
1549             my @r_id_properties;
1550              
1551 4683         12062 for (my $n=0; $n<@$id_properties; $n++) {
1552 5124         6988 my $id_property_name = $id_properties->[$n];
1553 5124         6471 my $id_property_detail = $properties->{$id_property_name};
1554 5124 50       9028 unless ($id_property_detail) {
1555             #$DB::single = 1;
1556 0         0 1;
1557             }
1558              
1559             # No data_type specified, first try parent classes for the same property name
1560             # and use their type
1561 5124 100 66     16407 if (!$bootstrapping and !exists($id_property_detail->{data_type})) {
1562 1569 100       7308 if (my $inh_prop = ($self->ancestry_property_metas(property_name => $id_property_name))[0]) {
1563 128         570 $id_property_detail->{data_type} = $inh_prop->data_type;
1564             }
1565             }
1566              
1567             # Didn't find one - use the data type of the ID property(s) in the class we point to
1568 5124 100       14296 unless ($id_property_detail->{data_type}) {
1569 1829 100       4502 unless ($r_class) {
1570             # FIXME - it'd be nice if we didn't have to load the remote class here, and
1571             # instead put off loading until it's necessary
1572 1389   66     7770 $r_class ||= UR::Object::Type->get($r_class_name);
1573 1389 100       3448 unless ($r_class) {
1574 1         119 Carp::confess("Unable to load $r_class_name while defining relationship ".$pinfo->{'property_name'}. " in class $class_name");
1575             }
1576 1388         8461 @r_id_properties = $r_class->id_property_names;
1577             }
1578             my ($r_property) =
1579             map {
1580 1828         5959 my $r_class_ancestor = UR::Object::Type->get($_);
  3520         7684  
1581 3520         6731 my $data = $r_class_ancestor->{has}{$r_id_properties[$n]};
1582 3520 100       7564 ($data ? ($data) : ());
1583             }
1584             ($r_class_name, $r_class_name->__meta__->ancestry_class_names);
1585 1828 50       4043 unless ($r_property) {
1586             #$DB::single = 1;
1587 0         0 my $property_name = $pinfo->{'property_name'};
1588 0 0       0 if (@$id_properties != @r_id_properties) {
1589 0         0 Carp::croak("Can't resolve relationship for class $class_name property '$property_name': "
1590             . "id_by metadata has " . scalar(@$id_properties) . " items, but remote class "
1591             . "$r_class_name only has " . scalar(@r_id_properties) . " ID properties\n");
1592             } else {
1593 0 0       0 my $r_id_property = $r_id_properties[$n] ? "'$r_id_properties[$n]'" : '(undef)';
1594 0         0 Carp::croak("Can't resolve relationship for class $class_name property '$property_name': "
1595             . "Class $r_class_name does not have an ID property named $r_id_property, "
1596             . "which would be linked to the local property '".$id_properties->[$n]."'\n");
1597             }
1598             }
1599 1828         7329 $id_property_detail->{data_type} = $r_property->{data_type};
1600             }
1601             }
1602 4682         6657 next;
1603             }
1604              
1605             # make old-style (bc4nf) property objects in the default way
1606 24686         24756 my %property_objects;
1607              
1608 24686         41104 for my $pinfo (values %$properties) {
1609 95379         122664 my $property_name = $pinfo->{property_name};
1610 95379         86185 my $property_subclass = $pinfo->{property_subclass};
1611              
1612             # Acme::Employee::Attribute::Name is a bc6nf attribute
1613             # extends Acme::Employee::Attribute
1614             # extends UR::Object::Attribute
1615             # extends UR::Object
1616 95379         192516 my @words = map { ucfirst($_) } split(/_/,$property_name);
  237876         348230  
1617             #@words = $self->namespace->get_vocabulary->convert_to_title_case(@words);
1618 95379         201825 my $bridge_class_name =
1619             $class_name
1620             . "::Attribute::"
1621             . join('', @words);
1622              
1623             # Acme::Employee::Attribute::Name::Type is both the class definition for the bridge,
1624             # and also the attribute/property metadata for
1625 95379         90125 my $property_meta_class_name = $bridge_class_name . "::Type";
1626              
1627             # define a new class for the above, inheriting from UR::Object::Property
1628             # all of the "attributes_have" get put into the class definition
1629             # call the constructor below on that new class
1630             #UR::Object::Type->__define__(
1631             ## class_name => $property_meta_class_name,
1632             # is => 'UR::Object::Property', # TODO: go through the inheritance
1633             # has => [
1634             # @{ $class_name->__meta__->{attributes_have} }
1635             # ]
1636             #)
1637              
1638 95379         68696 my ($singular_name,$plural_name);
1639 95379 100 66     202816 unless ($pinfo->{plural_name} and $pinfo->{singular_name}) {
1640 80063         320423 require Lingua::EN::Inflect;
1641 80063 100       116981 if ($pinfo->{is_many}) {
1642 11179   33     39539 $plural_name = $pinfo->{plural_name} ||= $pinfo->{property_name};
1643 11179         25381 $pinfo->{singular_name} = Lingua::EN::Inflect::PL_V($plural_name);
1644             }
1645             else {
1646 68884   33     217877 $singular_name = $pinfo->{singular_name} ||= $pinfo->{property_name};
1647 68884         154569 $pinfo->{plural_name} = Lingua::EN::Inflect::PL($singular_name);
1648             }
1649             }
1650              
1651 95379         14052347 my $property_object = UR::Object::Property->__define__(%$pinfo, id => $class_name . "\t" . $property_name);
1652              
1653 95379 50       238517 unless ($property_object) {
1654 0         0 $self->error_message("Error creating property $property_name for class " . $self->class_name . ": " . $class_name->error_message);
1655 0         0 for $property_object (@subordinate_objects) { $property_object->unload }
  0         0  
1656 0         0 $self->unload;
1657 0         0 return;
1658             }
1659              
1660 95379         118277 $property_objects{$property_name} = $property_object;
1661 95379         182344 push @subordinate_objects, $property_object;
1662             }
1663              
1664 24686 50       47247 if ($constraints) {
1665 24686         86755 my $property_rule_template = UR::BoolExpr::Template->resolve('UR::Object::Property','class_name','property_name');
1666              
1667 24686         32556 my $n = 1;
1668 24686         57574 for my $unique_set (sort { $a->{sql} cmp $b->{sql} } @$constraints) {
  0         0  
1669 271         641 my ($name,$properties,$group,$sql);
1670 271 50       1413 if (ref($unique_set) eq "HASH") {
1671 271         714 $name = $unique_set->{name};
1672 271         557 $properties = $unique_set->{properties};
1673 271         586 $sql = $unique_set->{sql};
1674 271   33     2551 $name ||= $sql;
1675             }
1676             else {
1677 0         0 $properties = @$unique_set;
1678 0         0 $name = '(unnamed)';
1679 0         0 $n++;
1680             }
1681 271         1431 for my $property_name (sort @$properties) {
1682 537         2130 my $prop_rule = $property_rule_template->get_rule_for_values($class_name,$property_name);
1683 537         2224 my $property = $UR::Context::current->get_objects_for_class_and_rule('UR::Object::Property', $prop_rule);
1684 537 50       2031 unless ($property) {
1685 0         0 Carp::croak("Constraint '$name' on class $class_name requires unknown property '$property_name'");
1686             }
1687             }
1688             }
1689             }
1690              
1691 24686         36242 for my $obj ($self,@subordinate_objects) {
1692             #use Data::Dumper;
1693 268     267   1474 no strict;
  273         456  
  267         154384  
1694 120065         856515 my %db_committed = %$obj;
1695 120065         259907 delete @db_committed{@keys_to_delete_from_db_committed};
1696 120065         285257 $obj->{'db_committed'} = \%db_committed;
1697              
1698             };
1699              
1700 24686 50       106873 unless ($self->generate) {
1701 0         0 $self->error_message("Error generating class " . $self->class_name . " as part of creation : " . $self->error_message);
1702 0         0 for my $property_object (@subordinate_objects) { $property_object->unload }
  0         0  
1703 0         0 $self->unload;
1704 0         0 return;
1705             }
1706              
1707 24686 100       66612 if (my $extra = $self->{extra}) {
1708 263         1346 $self->_apply_extra_attrs_to_class_or_role($extra);
1709             }
1710              
1711 24686         105747 $self->__signal_change__("load");
1712              
1713              
1714 24686         325963 my @i = $class_name->inheritance;
1715              
1716 24686         35904 for my $parent_class_name (@i) {
1717 88985 100       248350 if ($parent_class_name->can('__signal_observers__')) {
1718 61219         387527 $parent_class_name->__signal_observers__('subclass_loaded', $class_name);
1719             }
1720             }
1721              
1722             # The inheritance method is high overhead because of the number of times it is called.
1723             # Cache on a per-class basis.
1724 24686 50       652924 if (grep { $_ eq '' } @i) {
  88985         122752  
1725 0         0 print "$class_name! @{ $self->{is} }";
  0         0  
1726 0         0 $class_name->inheritance;
1727             }
1728 24686 50       84873 Carp::confess("Odd inheritance @i for $class_name") unless $class_name->isa('UR::Object');
1729 24686         53525 my $src1 = " return shift->SUPER::inheritance(\@_) if ( (ref(\$_[0])||\$_[0]) ne '$class_name'); return (" . join(", ", map { "'$_'" } (@i)) . ")";
  88985         166268  
1730 24686         57197 my $src2 = qq|sub ${class_name}::inheritance { $src1 }|;
1731 24686 100 66 647 1 2521275 eval $src2 unless $class_name eq 'UR::Object';
  647 100 66 946 1 4583  
  222 100 66 620 1 706  
  946 100 66 768 1 5576  
  459 100 66 584 1 1343  
  620 100 66 617 1 4884  
  262 100 66 990 1 991  
  768 100 66 645 1 5331  
  263 100 66 531 1 878  
  584 100 66 728 1 4311  
  205 100 66 668 1 683  
  617 100 33 740 1 4256  
  230 100 66 897 1 745  
  990 100 66 517 1 5819  
  500 100 66 729 1 1431  
  645 100 66 603 1 4756  
  197 100 33 787 1 629  
  531 100 33 642 1 3785  
  236 100 66 652 1 849  
  728 100 66 533 1 5546  
  189 100 66 644 1 641  
  668 100 33 552 1 4856  
  190 100 66 511 1 598  
  740 100 66 593 1 5591  
  200 100 66 923 1 608  
  897 100 33 535 1 5078  
  478 100 66 517 1 1219  
  517 100 66 595 1 3605  
  196 100 66 560 1 647  
  729 100 66 516 1 5147  
  221 100 66 611 1 697  
  603 100 66 904 1 4524  
  188 100 33 668 1 624  
  787 100 33 699 1 5479  
  281 100 66 686 1 841  
  642 100 66 665 1 4788  
  183 100 66 597 1 651  
  652 100 66 748 1 4666  
  210 100 66 578 1 707  
  533 100 66 590 1 3914  
  233 100 66 771 1 794  
  644 100 33 580 1 4741  
  214 100 33 831 1 727  
  552 100 66 482 1 3996  
  200 100 66 777 1 632  
  511 100 66 667 1 3435  
  250 100 33 537 1 765  
  593 100 66 605 1 4102  
  238 100 33 695 1 867  
  923 100 66 709 1 5437  
  526 100 66 589 1 1357  
  535 100 33 552 1 3912  
  201 100 33 612 1 697  
  517 100 33 629 1 3727  
  199 100 66 582 1 686  
  595 100 33 769 1 3946  
  236 100 66 652 1 802  
  560 100 66 594 1 4238  
  211 100 66 582 1 685  
  516 100 66 744 1 3742  
  211 100 66 580 1 693  
  611 100 66 706 1 4451  
  212 100 66 703 1 704  
  904 100 33 565 1 5647  
  496 100 33 556 1 1799  
  668 100 33 624 1 5059  
  206 100 66 768 1 692  
  699 100 66 652 1 5050  
  196 100 66 618 1 676  
  686 100 66 648 1 4702  
  262 100 66 620 1 847  
  665 100 66 772 1 4792  
  207 100 66 649   663  
  597 100 33 577   4182  
  223 100 33 569   778  
  748 100 66 593   5535  
  230 100 66 492   727  
  578 100 33 429   4185  
  224 100 66 692   734  
  590 100 66 485   4347  
  203 100 66 1417140   683  
  771 100 66 1389022   5206  
  286 100 66 1385096   849  
  580 100 66 1352399   4297  
  216 100 66 1352399   679  
  831 100 66 1322637   6191  
  220 100 66 1322637   705  
  482 100 33 1264281   3492  
  222 100 66 1264281   717  
  777 100 66 1200024   5236  
  280 100 66 1200024   880  
  667 100 66 1114183   4852  
  230 100 66 1114183   847  
  537 100 66 1040228   3890  
  192 100 66 1040228   626  
  605 100 66 970121   4543  
  205 100 66 970121   703  
  695 100 66 868294   4974  
  223 100 66 868294   709  
  709 100 33 791465   4940  
  289 100 66 785534   1034  
  589 100 33 726525   4268  
  215 100 66 726525   708  
  552 100 66 684568   4240  
  182 100 33 684568   570  
  612 100 66 630998   4588  
  205 100 66 630998   653  
  629 50 66 567725   4604  
  200 100 66 567725   676  
  582 100 66 435200   4344  
  191 50 66 435200   651  
  769 100 33 411730   5263  
  222 100 66 411730   677  
  652 100 66 347913   4562  
  231 100 33 347913   780  
  594 100 33 338684   4202  
  220 100 33 338684   747  
  582 50 66 298112   4467  
  259 100 33 288693   860  
  744 100 33 288693   5225  
  248 100 66 288693   888  
  580 100 33 288693   4064  
  235 100 33 288693   751  
  706 100 66 288693   5048  
  235 100 33 288693   822  
  703 100 33 280227   5120  
  238 50 33 280227   807  
  565 100 33 174099   4125  
  184 100 66 174099   638  
  556 100 33 165583   3863  
  232 50 33 165583   730  
  624 100 33 139849   4608  
  214 100 33 139849   694  
  768 100 33 139849   4471  
  446 100 33 139849   1485  
  652 100 33 56296   4568  
  347 50 33 56296   1221  
  618 50 33 56296   4408  
  182 100 33 56296   608  
  648 100 33 56296   4717  
  203 100 33 56296   671  
  620 100 33 56296   4561  
  191 100 33 56296   633  
  772 100 33 56296   5862  
  188 50 33 56296   614  
  649   33 39925   4497  
  278   33 39925   1050  
  577   33 39925   4039  
  200   33 39925   622  
  569   33 39925   4205  
  169   33 39925   565  
  593   33 39925   4632  
  213   33 39925   884  
  492   33 39925   3580  
  172   33 39925   603  
  429   33 39925   3054  
  173   33 39925   575  
  692   33 39925   4872  
  205   33 39925   700  
  485   33 39925   3485  
  157   33 39925   587  
  105   33 39925   2948  
  23   33 39925   68  
  76   33 39925   535  
  37   33 39925   143  
  78   33 39925   615  
  18   33 39925   66  
  95   33 31069   645  
  23   33 31069   113  
  12         81  
  6         22  
  14         90  
  8         23  
  5         32  
  5         14  
  8         51  
  2         6  
  21         129  
  15         50  
  3         23  
  3         10  
  21         176  
  3         10  
  18         121  
  6         24  
  15         88  
  6         19  
  12         72  
  3         11  
  6         56  
  3         10  
  6         78  
  3         9  
  2         14  
  2         8  
  7         47  
  4         12  
  12         88  
  3         11  
  12         84  
  3         8  
  11         64  
  4         11  
  25         138  
  19         63  
  9         53  
  3         9  
  11         61  
  3         9  
  12         69  
  3         8  
  2         14  
  2         8  
  5         28  
  2         5  
  24         194  
  3         11  
  7         52  
  2         6  
  2         41  
  2         6  
  40         240  
  7         21  
  9         69  
  3         11  
  11         70  
  2         7  
  6         40  
  3         9  
  8         51  
  2         6  
  2         12  
  2         6  
  2         14  
  2         5  
  12         71  
  3         9  
  17         119  
  1         2  
  5         29  
  2         7  
  4         35  
  1         3  
  4         24  
  1         3  
  6         39  
  3         9  
  2         13  
  2         5  
1732 24686 50       64958 die $@ if $@;
1733              
1734 24686         56334 $self->{'_property_meta_for_name'} = \%property_objects;
1735              
1736             # return the new class object
1737 24686         194226 return $self;
1738             }
1739              
1740             sub _apply_extra_attrs_to_class_or_role {
1741 779     779   4103 my($self, $extra) = @_;
1742              
1743 469 100       1759 if ($extra) {
1744             # some class characteristics may be only present in subclasses of UR::Object
1745             # we handle these at this point, since the above is needed for bootstrapping
1746 765         4420 my %still_not_found;
1747 420         1774 for my $key (sort keys %$extra) {
1748 784 100       5194 if ($self->can($key)) {
1749 458         3429 $self->$key($extra->{$key});
1750             }
1751             else {
1752 769         4662 $still_not_found{$key} = $extra->{$key};
1753             }
1754             }
1755 767 100       2475 if (%still_not_found) {
1756 426 100       2894 my $kind = $self->isa('UR::Object::Type')
1757             ? 'Class'
1758             : 'Role';
1759 195         704 my $name = $self->id;
1760              
1761 439         3138 $DB::single = 1;
1762             Carp::croak("Bad $kind defninition for $name. Unrecognized properties:\n\t"
1763 161         556 . join("\n\t", join(' => ', map { ($_, $still_not_found{$_}) } keys %still_not_found)));
  673         4696  
1764             }
1765             }
1766              
1767              
1768             }
1769              
1770             # write the module from the existing data in the class object
1771             sub generate {
1772 25745     25986 1 31801 my $self = shift;
1773 25929 100       58295 return 1 if $self->{'generated'};
1774              
1775             #my %params = @_; # Doesn't seem to be used below...
1776              
1777              
1778             # The follwing code will override a lot intentionally.
1779             # Supress the warning messages.
1780 289     267   1562 no warnings;
  274         444  
  267         23196  
1781              
1782             # the class that this object represents
1783             # the class that we're going to generate
1784             # the "new class"
1785 24845         65947 my $class_name = $self->class_name;
1786              
1787             # this is done earlier in the class definition process in _make_minimal_class_from_normalized_class_description()
1788 25092         51422 my $full_name = join( '::', $class_name, '__meta__' );
1789             Sub::Install::reinstall_sub({
1790             into => $class_name,
1791             as => '__meta__',
1792 1736606     1736704   2656687 code => Sub::Name::subname $full_name => sub {$self},
        1736952      
        1736609      
        1736622      
        1736856      
        1736628      
        1736590      
        1736606      
        1736679      
        1736492      
        1736655      
        1736530      
        1736577      
        1736503      
        1736470      
        1736443      
        1736490      
        1736504      
        1736355      
        1736285      
        1736352      
        1736388      
        1736328      
        1736424      
        1736444      
        1736321      
        1736327      
        1736347      
        1736333      
        1736272      
        1736315      
        1736304      
        1736263      
        1736246      
        1736239      
        1736299      
        1736263      
        1736242      
        1736213      
        1736236      
        1736231      
        1736211      
        1736219      
        1736283      
        1736254      
        1736262      
        1736279      
        1736196      
        1736198      
        1736189      
        1736192      
        1736205      
        1736187      
        1736205      
        1736202      
        1736199      
        1736196      
        1736190      
        1736190      
        1736186      
        1736191      
        1736196      
        1736196      
        1736195      
        1714869      
        1714853      
        1692615      
        1690004      
        1674073      
        1670902      
        1655457      
        1630648      
        1613132      
        1610192      
        1604095      
        1540660      
        1534469      
        1521600      
        1521594      
        1487554      
        1487564      
        1450490      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        31069      
        16935      
        16935      
        16935      
        16935      
        16935      
        16935      
        16935      
        16935      
1793 24851         274462 });
1794              
1795 24844         1849670 my @parent_class_names = $self->parent_class_names;
1796              
1797 25181         31158 do {
1798 267     267   1232 no strict 'refs';
  276         501  
  267         8566  
1799 24870 100       26856 if (@{ $class_name . '::ISA' }) {
  24994         96910  
1800             #print "already have isa for class_name $class_name: " . join(",",@{ $class_name . '::ISA' }) . "\n";
1801             }
1802             else {
1803 266     268   967 no strict 'refs';
  266         406  
  266         153703  
1804 133         445 @{ $class_name . '::ISA' } = @parent_class_names;
  471         3435  
1805             #print "setting isa for class_name $class_name: " . join(",",@{ $class_name . '::ISA' }) . "\n";
1806             };
1807             };
1808              
1809              
1810 24878         44619 my ($props, $cols) = ([], []); # for _all_properties_columns()
1811 25032         57352 $self->{_all_properties_columns} = [$props, $cols];
1812              
1813 24789         30470 my $id_props = []; # for _all_id_properties()
1814 25079         41540 $self->{_all_id_properties} = $id_props;
1815              
1816             # build the supplemental classes
1817 24809         38417 for my $parent_class_name (@parent_class_names) {
1818 26305 100       61322 next if $parent_class_name eq "UR::Object";
1819              
1820 20873 100       39995 if ($parent_class_name eq $class_name) {
1821 286         2042 Carp::confess("$class_name has parent class list which includes itself?: @parent_class_names\n");
1822             }
1823              
1824 20847         65629 my $parent_class_meta = UR::Object::Type->get(class_name => $parent_class_name);
1825              
1826 20996 100       44909 unless ($parent_class_meta) {
1827             #$DB::single = 1;
1828 161         553 $parent_class_meta = UR::Object::Type->get(class_name => $parent_class_name);
1829 306         2185 Carp::confess("Cannot generate $class_name: Failed to find class meta-data for base class $parent_class_name.");
1830             }
1831              
1832 20835 100       67082 unless ($parent_class_meta->generated()) {
1833 1119         9025 $parent_class_meta->generate();
1834             }
1835              
1836 20813 100       49240 unless ($parent_class_meta->{_all_properties_columns}) {
1837 171         1119 Carp::confess("No _all_properties_columns for $parent_class_name?");
1838             }
1839              
1840             # inherit properties and columns
1841 20821         21824 my ($p, $c) = @{ $parent_class_meta->{_all_properties_columns} };
  20838         36785  
1842 20798 100       48151 push @$props, @$p if $p;
1843 20905 100       40235 push @$cols, @$c if $c;
1844 20830         25733 my $id_p = $parent_class_meta->{_all_id_properties};
1845 20941 100       51967 push @$id_props, @$id_p if $id_p;
1846             }
1847              
1848              
1849             # set up accessors/mutators for properties
1850 24815         67189 my @property_objects =
1851             UR::Object::Property->get(class_name => $self->class_name);
1852              
1853 24830         127760 my @id_property_objects = $self->direct_id_property_metas;
1854 24755         29142 my %id_property;
1855 24926         38613 for my $ipo (@id_property_objects) {
1856 21679         62503 $id_property{$ipo->property_name} = 1;
1857             }
1858              
1859 24946 100       54791 if (@id_property_objects) {
1860 18474         28776 $id_props = [];
1861 18444         29928 for my $ipo (@id_property_objects) {
1862 21645         45667 push @$id_props, $ipo->property_name;
1863             }
1864             }
1865              
1866 24829         27050 my $has_table;
1867 24740         36505 my @parent_classes = map { UR::Object::Type->get(class_name => $_) } @parent_class_names;
  26149         77419  
1868 24751         43613 for my $co ($self, @parent_classes) {
1869 50334 100       161812 if ($co->table_name) {
1870 577         1151 $has_table = 1;
1871 601         1583 last;
1872             }
1873             }
1874              
1875 24727         81257 my $data_source_obj = $self->data_source;
1876 24817         26694 my $columns_are_upper_case;
1877 24730 100       46436 if ($data_source_obj) {
1878 2222         10924 $columns_are_upper_case = $data_source_obj->table_and_column_names_are_upper_case;
1879             }
1880              
1881 24718         39694 my @sort_list = map { [$_->property_name, $_] } @property_objects;
  76567         105903  
1882 24720         61898 for my $sorted_item ( sort { $a->[0] cmp $b->[0] } @sort_list ) {
  87883         72586  
1883 76513         53048 my $property_object = $sorted_item->[1];
1884 76543 100       101791 if ($property_object->column_name) {
1885 1854         3133 push @$props, $property_object->property_name;
1886 1950 100       5171 push @$cols, $columns_are_upper_case ? uc($property_object->column_name) : $property_object->column_name;
1887             }
1888             }
1889              
1890             # set the flag to prevent this from occurring multiple times.
1891 24739         89793 $self->generated(1);
1892              
1893             # read in filesystem package if there is one
1894             #$self->use_filesystem_package($class_name);
1895              
1896             # Let each class in the inheritance hierarchy do any initialization
1897             # required for this class. Note that the _init_subclass method does
1898             # not call SUPER::, but relies on this code to find its parents. This
1899             # is the only way around a sparsely-filled multiple inheritance tree.
1900              
1901             # TODO: Replace with $class_name->EVERY::LAST::_init_subclass()
1902              
1903             #unless (
1904             # $bootstrapping
1905             # and
1906             # $UR::Object::_init_subclass->{$class_name}
1907             #)
1908             {
1909 24765         22935 my @inheritance = $class_name->inheritance;
  24706         511344  
1910 24744         30489 my %done;
1911 24708         35113 for my $parent (reverse @inheritance) {
1912 89014         216667 my $initializer = $parent->can("_init_subclass");
1913 88999 100       2702675 next unless $initializer;
1914 4406 100       15397 next if $done{$initializer};
1915 2685 100       10564 $initializer->($class_name,$class_name)
1916             or die "Parent class $parent failed to initialize subclass "
1917             . "$class_name :" . $parent->error_message;
1918 2704         10835 $done{$initializer} = 1;
1919             }
1920             }
1921              
1922 24712 100       144992 unless ($class_name->isa("UR::Object")) {
1923 27         191 print Data::Dumper::Dumper('@C::ISA',\@C::ISA,'@B::ISA',\@B::ISA);
1924             }
1925              
1926             # ensure the class is generated
1927 24704 100       59826 die "Error in module for $class_name. Resulting class does not appear to be generated!" unless $self->generated;
1928              
1929             # ensure the class inherits from UR::Object
1930 24721 100       71846 die "$class_name does not inherit from UR::Object!" unless $class_name->isa("UR::Object");
1931              
1932 24700         115327 return 1;
1933             }
1934              
1935              
1936             1;
1937              
1938