File Coverage

lib/UR/Object/Type.pm
Criterion Covered Total %
statement 69 89 77.5
branch 17 32 53.1
condition 10 20 50.0
subroutine 11 13 84.6
pod 2 3 66.6
total 109 157 69.4


line stmt bran cond sub pod time code
1             package UR::Object::Type;
2              
3 266     266   1001 use warnings;
  266         297  
  266         7693  
4 266     266   879 use strict;
  266         273  
  266         14437  
5             require UR;
6              
7             # Used during bootstrapping.
8             our @ISA = qw(UR::Object);
9             our $VERSION = "0.46"; # UR $VERSION;;
10              
11             our @CARP_NOT = qw( UR::Object UR::Context UR::ModuleLoader Class::Autouse UR::BoolExpr );
12              
13             # Most of the API for this module are legacy internals required by UR.
14 266     266   124329 use UR::Object::Type::InternalAPI;
  266         910  
  266         4738  
15              
16             # This module implements define(), and most everything behind it.
17 266     266   198916 use UR::Object::Type::Initializer;
  266         648  
  266         3490  
18              
19             # The methods used by the initializer to write accessors in perl.
20 266     266   166193 use UR::Object::Type::AccessorWriter;
  266         641  
  266         3530  
21              
22             # The methods to extract/(re)create definition text in the module source file.
23 266     266   126853 use UR::Object::Type::ModuleWriter;
  266         2183  
  266         6982  
24              
25             # Present the internal definer as an external method
26 12892     12892 1 443826 sub define { shift->__define__(@_) }
27              
28             # For efficiency, certain hash keys inside the class cache property metadata
29             # These go in this array, and are cleared when property metadata is mutated
30             our @cache_keys;
31              
32             # This is the function behind $class_meta->properties(...)
33             # It mimics the has-many object accessor, but handles inheritance
34             # Once we have "isa" and "is-parent-of" operator we can do this with regular operators.
35             push @cache_keys, '_properties';
36             sub _properties {
37 240     240   991 my $self = shift;
38 240   66     933 my $all = $self->{_properties} ||= do {
39             # start with everything, as it's a small list
40 136         863 my $map = $self->_property_name_class_map;
41 136         200 my @all;
42 136         720 for my $property_name (sort keys %$map) {
43 991         874 my $class_names = $map->{$property_name};
44 991         905 my $class_name = $class_names->[0];
45 991         1237 my $id = $class_name . "\t" . $property_name;
46 991         1735 my $property_meta = UR::Object::Property->get($id);
47 991 50       1504 unless ($property_meta) {
48 0         0 Carp::confess("Failed to find property meta for $class_name $property_name?");
49             }
50 991         1276 push @all, $property_meta;
51             }
52 136         510 \@all;
53             };
54 240 100       616 if (@_) {
55 121         744 my ($bx, %extra) = UR::Object::Property->define_boolexpr(@_);
56 121         292 my @matches = grep { $bx->evaluate($_) } @$all;
  811         1501  
57 121 100       397 if (%extra) {
58             # Additional meta-properties on meta-properties are not queryable until we
59             # put the UR::Object::Property into a private sub-class.
60             # This will give us most of the functionality.
61 4         11 for my $key (keys %extra) {
62 4         24 my ($name,$op) = ($key =~ /(\w+)\s*(.*)/);
63 4 100       18 unless (defined $self->{attributes_have}->{$name}) {
64 1         8 die "unknown property $name used to query properties of " . $self->class_name;
65             }
66 3 0 33     12 if ($op and $op ne '==' and $op ne 'eq') {
      33        
67 0         0 die "operations besides equals are not supported currently for added meta-properties like $name on class " . $self->class_name;
68             }
69 3         7 my $value = $extra{$key};
70 266     266   93415 no warnings;
  266         621  
  266         141221  
71 3 100       8 @matches = grep { $_->can($name) and $_->$name eq $value } @matches;
  7         25  
72             }
73             }
74 120 50       422 return if not defined wantarray;
75 120 100       585 return @matches if wantarray;
76 7 50       20 die "Matched multiple meta-properties, but called in scalar context!" . Data::Dumper::Dumper(\@matches) if @matches > 1;
77 7         21 return $matches[0];
78             }
79             else {
80 119         486 @$all;
81             }
82             }
83              
84             sub property {
85 446 50   446 1 4300 if (@_ == 2) {
86             # optimize for the common case
87 446         554 my ($self, $property_name) = @_;
88 446         1319 my $class_names = $self->_property_name_class_map->{$property_name};
89 446 100 66     1912 return unless $class_names and @$class_names;
90 414         874 my $id = $class_names->[0] . "\t" . $property_name;
91 414         1079 return UR::Object::Property->get($id);
92             }
93             else {
94             # this forces scalar context, raising an exception if
95             # the params used result in more than one match
96 0         0 my $one = shift->properties(@_);
97 0         0 return $one;
98             }
99             }
100              
101             push @cache_keys, '_property_names';
102             sub property_names {
103 0     0 0 0 my $self = $_[0];
104 0   0     0 my $names = $self->{_property_names} ||= do {
105 0         0 my @names = sort keys %{ shift->_property_name_class_map };
  0         0  
106 0         0 \@names;
107             };
108 0         0 return @$names;
109             }
110              
111             push @cache_keys, '_property_name_class_map';
112             sub _property_name_class_map {
113 582     582   714 my $self = shift;
114 582   66     1737 my $map = $self->{_property_name_class_map} ||= do {
115 214         483 my %map = ();
116 214         909 for my $class_name ($self->class_name, $self->ancestry_class_names) {
117 731         1814 my $class_meta = UR::Object::Type->get($class_name);
118 731 50       1671 if (my $has = $class_meta->{has}) {
119 731         2242 for my $key (sort keys %$has) {
120 1433   100     4489 my $classes = $map{$key} ||= [];
121 1433         2204 push @$classes, $class_name;
122             }
123             }
124             }
125 214         733 \%map;
126             };
127 582         1038 return $map;
128             }
129              
130             # The prior implementation of _properties() (behind ->properties())
131             # filtered out certain property meta. This is the old version.
132             # The new version above will return one object per property name in
133             # the meta ancestry.
134             sub _legacy_properties {
135 0     0     my $self = shift;
136 0 0         if (@_) {
137 0           my $bx = UR::Object::Property->define_boolexpr(@_);
138 0           my @matches = grep { $bx->evaluate($_) } $self->property_metas;
  0            
139 0 0         return if not defined wantarray;
140 0 0         return @matches if wantarray;
141 0 0         die "Matched multiple meta-properties, but called in scalar context!" . Data::Dumper::Dumper(\@matches) if @matches > 1;
142 0           return $matches[0];
143             }
144             else {
145 0           $self->property_metas;
146             }
147             }
148              
149             1;
150