File Coverage

lib/UR/BoolExpr/Util.pm
Criterion Covered Total %
statement 100 106 94.3
branch 46 54 85.1
condition 21 26 80.7
subroutine 18 18 100.0
pod 0 5 0.0
total 185 209 88.5


line stmt bran cond sub pod time code
1              
2             package UR::BoolExpr::Util;
3              
4             # Non-OO Utility methods for the rule modules.
5              
6 266     266   988 use strict;
  266         2197  
  266         12701  
7 266     266   2827 use warnings;
  266         284  
  266         15531  
8             require UR;
9             our $VERSION = "0.46"; # UR $VERSION;
10              
11 266     266   3030 use Scalar::Util qw(blessed reftype refaddr);
  266         351  
  266         14562  
12 266     266   1031 use Data::Dumper;
  266         294  
  266         13535  
13 266     266   135941 use FreezeThaw;
  266         1071116  
  266         57325  
14              
15             # Because the id is actually a full data structure we need some separators.
16             # Note that these are used for the common case, where FreezeThaw is for arbitrarily complicated rule identifiers.
17              
18             our $id_sep = chr(29); # spearetes id property values instead of the old \t
19             our $record_sep = chr(30); # within a value_id, delimits a distinct values
20             our $unit_sep = chr(31); # seperates items within a single value
21              
22             our $null_value = chr(21); # used for undef/null
23             our $empty_string = chr(28); # used for ""
24             our $empty_list = chr(20); # used for []
25              
26             # These are used when there is any sort of complicated data in the rule.
27              
28             sub values_to_value_id_frozen {
29 1522     1522 0 4065 my $frozen = FreezeThaw::safeFreeze(@_);
30 1522         103760 return "F:" . $frozen;
31             }
32              
33             sub value_id_to_values_frozen {
34 1076     1076 0 1099 my $value_id = shift;
35 266     266   3081 no warnings 'redefine';
  266         358  
  266         124070  
36 1076         3500 local *FreezeThaw::copyContents = \&_FreezeThaw__copyContents;
37 1076         2662 return _fixup_ur_objects_from_thawed_data(FreezeThaw::thaw($value_id));
38             }
39              
40             # FreezeThaw::thaw calls copyContents when thawing out a data structure it's seen before to
41             # copy the contents from an already thawed version into a to-be-thawed container.
42             # But this doesn't work for code references, since it can't reconstitute it. Luckily, if
43             # it's been frozen with safeFreeze, then $first and $second both point to the correct
44             # coderef and there's nothing to do.
45             my $original_FreezeThaw__copyContents = \&FreezeThaw::copyContents;
46             sub _FreezeThaw__copyContents {
47 4     4   1080 my($first, $second) = @_;
48              
49 4 100 66     32 goto &$original_FreezeThaw__copyContents if (reftype($first) ne 'CODE' or reftype($second) ne 'CODE');
50              
51 1 50       6 if (refaddr($first) != refaddr($second)) {
52 0         0 my $ref = reftype($second);
53 0         0 Carp::croak("Don't know how to copyContents of type `$ref'");
54             }
55 1 50       2 if (ref($second) ne ref($first)) {
56 0         0 bless $_[0], ref $second; # Rebless
57             }
58 1         2 return $first;
59             }
60              
61             sub _fixup_ur_objects_from_thawed_data {
62 1431     1431   101354 my @values = @_;
63              
64 1431         1137 our $seen;
65 1431         1413 local $seen = $seen;
66 1431   100     4170 $seen ||= {};
67              
68             # For things that are UR::Objects (or used to be UR objects), swap the
69             # thawed/cloned one with one from the object cache
70             #
71             # This sub is localized inside _fixup_ur_objects_from_thawed_data so it's not called
72             # externally, and uses $_ as the thing to process, which is set in the foreach loop
73             # below - both as a performance speedup of# not having to prepare an argument list while
74             # processing a possibly deep data structure, and clarity of avoiding double dereferencing
75             # as this sub needs to mutate the item it's processing
76             my $process_it = sub {
77 355 100 100 355   774 if (blessed($_)
      66        
78             and (
79             $_->isa('UR::Object')
80             or
81             $_->isa('UR::BoolExpr::Util::clonedThing')
82             )
83             ) {
84 20         163 my($class, $id) = ($_->class, $_->id);
85 20 50       94 if (refaddr($_) != refaddr($UR::Context::all_objects_loaded->{$class}->{$id})) {
86             # bless the original thing to a non-UR::Object class so UR::Object::DESTROY
87             # doesn't run on it
88 20         52 my $cloned_thing = UR::BoolExpr::Util::clonedThing->bless($_);
89             # Swap in the object from the object cache
90 20         54 $_ = $UR::Context::all_objects_loaded->{$class}->{$id};
91             }
92              
93             }
94 355         901 _fixup_ur_objects_from_thawed_data($_);
95 1431         4848 };
96              
97 1431         1922 foreach my $data ( @values ) {
98 6199 100       7933 next unless ref $data; # Don't need to recursively inspect normal scalar data
99 177 100       459 next if $seen->{$data}++;
100              
101 172 50       306 if (ref $data) {
102 172         332 my $reftype = reftype($data);
103 172         153 my $iter;
104 172 100 66     645 if ($reftype eq 'ARRAY') {
    100          
    100          
105 73         115 foreach (@$data) {
106 152         168 &$process_it;
107             }
108             } elsif ($reftype eq 'HASH') {
109 54         102 foreach (values %$data) {
110 202         191 &$process_it;
111             }
112              
113             } elsif ($reftype eq 'SCALAR' or $reftype eq 'REF') {
114 1         2 local $_ = $$data;
115 1         3 &$process_it;
116             }
117             }
118             }
119 1431         11133 return @values;
120             }
121              
122             # These are used for the simple common-case rules.
123              
124             sub values_to_value_id {
125 774359     774359 0 712745 my $value_id = "O:";
126              
127 774359         752275 for my $value (@_) {
128              
129 266     266   1294 no warnings;# 'uninitialized';
  266         378  
  266         129015  
130 1600006 100       1729208 if (length($value)) {
    100          
131 1597571 100       1641075 if (ref($value) eq "ARRAY") {
132 3516 100       5481 if (@$value == 0) {
133 503         643 $value_id .= $empty_list;
134             }
135             else {
136 3013         3620 for my $value2 (@$value) {
137 5217 100       8986 if (not defined $value2 ) {
    50          
138 260         374 $value_id .= $null_value . $unit_sep;
139             }
140             elsif ($value2 eq "") {
141 0         0 $value_id .= $empty_string . $unit_sep;
142             }
143             else {
144 4957 50 66     24463 if (ref($value2) or index($value2, $unit_sep) >= 0 or index($value2, $record_sep) >= 0) {
      66        
145 63         175 return values_to_value_id_frozen(@_);
146             }
147 4894         7006 $value_id .= $value2 . $unit_sep;
148             }
149             }
150             }
151 3453         4137 $value_id .= $record_sep;
152             }
153             else {
154 1594055 100 100     6894583 if (ref($value) or index($value,$unit_sep) >= 0 or index($value,$record_sep) >= 0) {
      100        
155 1459         3334 return values_to_value_id_frozen(@_);
156             }
157 1592596         2341786 $value_id .= $value . $record_sep;
158             }
159             } elsif (not defined $value ) {
160 1661         2548 $value_id .= $null_value . $record_sep;
161             }
162             else {# ($value eq "") {
163 774         1176 $value_id .= $empty_string . $record_sep;
164             }
165             }
166 772837         1306374 return $value_id;
167             }
168              
169             sub value_id_to_values {
170 851418     851418 0 696256 my $value_id = shift;
171              
172 851418 50       1138705 unless (defined $value_id) {
173 0         0 Carp::confess('No value_id passed in to value_id_to_values()!?');
174             }
175              
176 851418         823893 my $method_identifier = substr($value_id,0,2);
177 851418         1141980 $value_id = substr($value_id, 2, length($value_id)-2);
178 851418 100       1217352 if ($method_identifier eq "F:") {
179 1076         2150 return value_id_to_values_frozen($value_id);
180             }
181              
182 850342         5103345 my @values = ($value_id =~ /(.*?)$record_sep/gs);
183 850342         1174389 for (@values) {
184 2152262 100       5588147 if (substr($_,-1) eq $unit_sep) {
    100          
    100          
    100          
185             #$_ = [split($unit_sep,$_)]
186 1444         8494 my @values2 = /(.*?)$unit_sep/gs;
187 1444         1903 $_ = \@values2;
188 1444         1851 for (@values2) {
189 2705 100       6469 if ($_ eq $null_value) {
    50          
190 206         294 $_ = undef;
191             }
192             elsif ($_ eq $empty_string) {
193 0         0 $_ = "";
194             }
195             }
196             }
197             elsif ($_ eq $null_value) {
198 176         257 $_ = undef;
199             }
200             elsif ($_ eq $empty_string) {
201 660         872 $_ = "";
202             }
203             elsif ($_ eq $empty_list) {
204 170         303 $_ = [];
205             }
206             }
207 850342         2157312 return @values;
208             }
209              
210             sub is_meta_param {
211 1111465     1111465 0 861244 my $param_name = shift;
212 1111465         2460870 return substr($param_name, 0, 1) eq '-';
213             }
214              
215             package UR::BoolExpr::Util::clonedThing;
216              
217             sub bless {
218 20     20   19 my($class, $thing) = @_;
219             # return $thing if ($thing->isa(__PACKAGE__));
220              
221 20         28 $thing->{__original_class} = $thing->class;
222 20         32 bless $thing, $class;
223             }
224              
225             sub id {
226 2     2   4 return shift->{id};
227             }
228              
229             sub class {
230 4     4   11 return shift->{__original_class};
231             }
232              
233             1;
234              
235             =pod
236              
237             =head1 NAME
238              
239             UR::BoolExpr::Util - non-OO module to collect utility functions used by the BoolExpr modules
240              
241             =cut
242