File Coverage

blib/lib/Rose/Class/MakeMethods/Set.pm
Criterion Covered Total %
statement 256 273 93.7
branch 100 146 68.4
condition 69 131 52.6
subroutine 36 36 100.0
pod 2 2 100.0
total 463 588 78.7


line stmt bran cond sub pod time code
1             package Rose::Class::MakeMethods::Set;
2              
3 3     7   3959 use strict;
  3         8  
  3         119  
4              
5 3     3   17 use Carp();
  3         6  
  3         112  
6              
7             our $VERSION = '0.81';
8              
9 3     3   25 use Rose::Object::MakeMethods;
  3         4  
  3         23  
10             our @ISA = qw(Rose::Object::MakeMethods);
11              
12             our %Inheritable_Set;
13             # (
14             # some_attr_name =>
15             # {
16             # class1 =>
17             # {
18             # meta => { ... },
19             # cache => { ... },
20             # },
21             # class2 => ...,
22             # ...
23             # },
24             # ...
25             # );
26              
27             sub inheritable_set
28             {
29 6     6 1 13 my($class, $name, $args) = @_;
30              
31 6         9 my %methods;
32              
33             # Interface example:
34             # name: required_html_attr
35             # plural_name: required_html_attrs
36             # list_method: required_html_attrs
37             # hash_method: required_html_attrs_hash
38             # test_method: is_required_html_attr (or html_attr_is_required)
39             # add_method: add_required_html_attr
40             # adds_method: add_required_html_attrs
41             # delete_method: delete_required_html_attr
42             # deletes_method: delete_required_html_attrs
43             # clear_method: clear_required_html_attrs
44              
45 6   33     37 my $plural_name = $args->{'plural_name'} || $name . 's';
46              
47 6   66     23 my $list_method = $args->{'list_method'} || $plural_name;
48 6   33     29 my $hash_method = $args->{'hash_method'} || $plural_name . '_hash';
49 6   66     61 my $test_method = $args->{'test_method'} || $args->{'test_method'} || 'is_' . $name;
50 6   66     24 my $add_method = $args->{'add_method'} || 'add_' . $name;
51 6   66     31 my $adds_method = $args->{'adds_method'} || $add_method . 's';
52 6   66     80 my $delete_method = $args->{'delete_method'} || 'delete_' . $name;
53 6   66     27 my $deletes_method = $args->{'deletes_method'} || 'delete_' . $plural_name;
54 6   66     43 my $clear_method = $args->{'clear_method'} || 'clear_' . $plural_name;
55 6   33     68 my $value_method = $args->{'value_method'} || $name . '_value';
56              
57 6   50     26 my $interface = $args->{'interface'} || 'all';
58 6         21 my $add_implies = $args->{'add_implies'};
59 6         10 my $delete_implies = $args->{'delete_implies'};
60              
61 6 100 66     39 $add_implies = [ $add_implies ]
62             if(defined $add_implies && !ref $add_implies);
63              
64 6 50 33     19 $delete_implies = [ $delete_implies ]
65             if(defined $delete_implies && !ref $delete_implies);
66              
67             $methods{$test_method} = sub
68             {
69 22 50   22   4726 my($class) = ref($_[0]) ? ref(shift) : shift;
70              
71 22 50       50 return 0 unless(defined $_[0]);
72              
73 3     3   18 no strict 'refs';
  3         8  
  3         1414  
74 22 100       66 return 1 if(exists $class->$hash_method()->{$_[0]});
75 4         18 return 0;
76 6         61 };
77              
78             $methods{$hash_method} = sub
79             {
80 67   33 67   207 my($class) = ref($_[0]) || $_[0];
81              
82 67 100       206 unless(exists $Inheritable_Set{$name}{$class})
83             {
84 3     3   20 no strict 'refs';
  3         7  
  3         157  
85              
86 14         41 my @parents = ($class);
87              
88 14         45 while(my $parent = shift(@parents))
89             {
90 3     3   15 no strict 'refs';
  3         5  
  3         2020  
91 30         43 foreach my $subclass (@{$parent . '::ISA'})
  30         144  
92             {
93 16         30 push(@parents, $subclass);
94              
95 16 100       71 if(exists $Inheritable_Set{$name}{$subclass})
96             {
97 8         12 while(my($k, $v) = each(%{$Inheritable_Set{$name}{$subclass}}))
  30         113  
98             {
99 22 100       59 next if(exists $Inheritable_Set{$name}{$class}{$k});
100 16         44 $Inheritable_Set{$name}{$class}{$k} = $v;
101             }
102             }
103             }
104             }
105             }
106              
107 67   100     299 $Inheritable_Set{$name}{$class} ||= {};
108 67 50       346 return wantarray ? %{$Inheritable_Set{$name}{$class}} :
  0         0  
109             $Inheritable_Set{$name}{$class};
110 6         40 };
111              
112             $methods{$list_method} = sub
113             {
114 14     14   1542 my($class) = shift;
115              
116 14 50       40 $class = ref $class if(ref $class);
117              
118 14 50       34 if(@_)
119             {
120 0         0 $class->$clear_method();
121 0         0 $class->$adds_method(@_);
122 0 0       0 return unless(defined wantarray);
123             }
124              
125 14         64 return wantarray ? sort keys %{$class->$hash_method()} :
  0         0  
126 14 50       35 [ sort keys %{$class->$hash_method()} ];
127 6         34 };
128              
129 6     3   24 $methods{$add_method} = sub { shift->$adds_method(@_) };
  3         19  
130              
131             $methods{$adds_method} = sub
132             {
133 9 50   9   3505 my($class) = ref($_[0]) ? ref(shift) : shift;
134 9 50       35 Carp::croak("Missing value(s) to add") unless(@_);
135              
136 9         19 my $count = 0;
137 9         39 my $req_hash = $class->$hash_method();
138              
139 9 100       48 return 0 unless(defined $_[0]);
140              
141 7         13 my %attrs;
142              
143 7         17 foreach my $arg (grep { defined } @_)
  13         39  
144             {
145 13 50       38 if(ref $arg eq 'HASH')
146             {
147 0         0 $attrs{$_} = $arg->{$_} for(keys %$arg);
148             }
149             else
150             {
151 13         39 $attrs{$arg} = undef;
152             }
153             }
154              
155 7         45 while(my($attr, $val) = each(%attrs))
156             {
157 3     3   18 no strict 'refs';
  3         8  
  3         839  
158 13 50       35 next unless(defined $attr);
159 13         24 $req_hash->{$attr} = $val;
160              
161 13 100       27 if($add_implies)
162             {
163 6         12 foreach my $method (@$add_implies)
164             {
165 6         20 $class->$method($attr);
166             }
167             }
168              
169 13         44 $count++;
170             }
171              
172 7         33 return $count;
173 6         37 };
174              
175             $methods{$clear_method} = sub
176             {
177 4 50   4   19 my($class) = ref($_[0]) ? ref(shift) : shift;
178 4         27 my @values = $class->$list_method();
179 4 100       20 return unless(@values);
180 2         18 $class->$deletes_method(@values);
181 6         26 };
182              
183 6     2   75 $methods{$delete_method} = sub { shift->$deletes_method(@_) };
  2         7027  
184              
185             $methods{$deletes_method} = sub
186             {
187 4 50   4   21 my($class) = ref($_[0]) ? ref(shift) : shift;
188 4 50       15 Carp::croak("Missing value(s) to delete") unless(@_);
189              
190 4         6 my $count = 0;
191 4         11 my $req_hash = $class->$hash_method();
192              
193 4         9 foreach my $attr (@_)
194             {
195 3     3   22 no strict 'refs';
  3         6  
  3         1141  
196 6 50       15 next unless(defined $attr);
197 6 50       18 next unless(exists $req_hash->{$attr});
198 6         11 delete $req_hash->{$attr};
199 6         7 $count++;
200              
201 6 50       40 if($delete_implies)
202             {
203 0         0 foreach my $method (@$delete_implies)
204             {
205 0         0 $class->$method($attr);
206             }
207             }
208             }
209              
210 4         17 return $count;
211 6         144 };
212              
213             $methods{$value_method} = sub
214             {
215 18   33 18   73 my($class) = ref($_[0]) || $_[0];
216              
217 18         59 my $hash = $class->$hash_method();
218 18 100 66     1444 return undef unless($_[1] && exists $hash->{$_[1]});
219 8 100       49 return $hash->{$_[1]} = $_[2] if(@_ > 2);
220 3         18 return $hash->{$_[1]};
221 6         32 };
222              
223 6 50       43 if($interface ne 'all')
224             {
225 0         0 Carp::croak "Unknown interface: $interface";
226             }
227              
228 6         27 return \%methods;
229             }
230              
231 3     3   18 use constant CLASS_VALUE => 1;
  3         6  
  3         229  
232 3     3   15 use constant INHERITED_VALUE => 2;
  3         5  
  3         132  
233 3     3   12 use constant DELETED_VALUE => 3;
  3         6  
  3         1510  
234              
235             our %Inherited_Set;
236             # (
237             # some_attr_name =>
238             # {
239             # class1 =>
240             # {
241             # meta => { ... },
242             # cache => { ... },
243             # },
244             # class2 => ...,
245             # ...
246             # },
247             # ...
248             # );
249              
250             sub inherited_set
251             {
252 4     4 1 6 my($class, $name, $args) = @_;
253              
254 4         8 my %methods;
255              
256             # Interface example:
257             # name: valid_html_attr
258             # plural_name: valid_html_attrs
259             # list_method: valid_html_attrs
260             # cache_method: valid_html_attrs_cache
261             # hash_method: valid_html_attrs_hash
262             # test_method: is_valid_html_attr (or html_attr_is_valid)
263             # add_method: add_valid_html_attr
264             # adds_method: add_valid_html_attrs
265             # delete_method: delete_valid_html_attr
266             # deletes_method: delete_valid_html_attrs
267             # clear_method clear_valid_html_attrs
268             # inherit_method: inherit_valid_html_attr
269             # inherits_method: inherit_valid_html_attrs
270              
271 4   33     24 my $plural_name = $args->{'plural_name'} || $name . 's';
272              
273 4   33     19 my $list_method = $args->{'list_method'} || $plural_name;
274 4   33     20 my $cache_method = $args->{'cache_method'} || $plural_name . '_cache';
275 4   33     19 my $hash_method = $args->{'hash_method'} || $plural_name . '_hash';
276 4   33     27 my $test_method = $args->{'test_method'} || $args->{'test_method'} || 'is_' . $name;
277 4   33     30 my $add_method = $args->{'add_method'} || 'add_' . $name;
278 4   33     21 my $adds_method = $args->{'adds_method'} || $add_method . 's';
279 4   33     21 my $delete_method = $args->{'delete_method'} || 'delete_' . $name;
280 4   33     39 my $deletes_method = $args->{'deletes_method'} || 'delete_' . $plural_name;
281 4   33     26 my $clear_method = $args->{'clear_method'} || 'clear_' . $plural_name;
282 4   33     110 my $inherit_method = $args->{'inherit_method'} || 'inherit_' . $name;
283 4   33     19 my $inherits_method = $args->{'inherits_method'} || $inherit_method . 's';
284              
285 4   50     23 my $interface = $args->{'interface'} || 'all';
286              
287 4         7 my $add_implies = $args->{'add_implies'};
288 4         8 my $delete_implies = $args->{'delete_implies'};
289 4         7 my $inherit_implies = $args->{'inherit_implies'};
290              
291 4 100 66     23 $add_implies = [ $add_implies ]
292             if(defined $add_implies && !ref $add_implies);
293              
294 4 100 66     25 $delete_implies = [ $delete_implies ]
295             if(defined $delete_implies && !ref $delete_implies);
296              
297 4 100 66     26 $inherit_implies = [ $inherit_implies ]
298             if(defined $inherit_implies && !ref $inherit_implies);
299              
300             $methods{$cache_method} = sub
301             {
302 200   33 200   529 my($class) = ref($_[0]) || $_[0];
303              
304 200 100       586 if($Inherited_Set{$name}{$class}{'meta'}{'cache_is_valid'})
305             {
306             return
307 112 100 50     460 wantarray ? (%{$Inherited_Set{$name}{$class}{'cache'} ||= {}}) :
  8   50     67  
308             ($Inherited_Set{$name}{$class}{'cache'} ||= {});
309             }
310              
311 88         154 my @parents = ($class);
312              
313 88         219 while(my $parent = shift(@parents))
314             {
315 3     3   24 no strict 'refs';
  3         89  
  3         3264  
316 194         196 foreach my $subclass (@{$parent . '::ISA'})
  194         709  
317             {
318 106         158 push(@parents, $subclass);
319              
320 106 50       521 if($subclass->can($cache_method))
    0          
321             {
322 106         266 my $cache = $subclass->$cache_method();
323              
324 106         350 while(my($attr, $val) = each %$cache)
325             {
326 440 100       1140 next if($val == DELETED_VALUE);
327 268 100       1562 $Inherited_Set{$name}{$class}{'cache'}{$attr} = INHERITED_VALUE
328             unless(exists $Inherited_Set{$name}{$class}{'cache'}{$attr});
329             }
330             }
331             # Slower method for subclasses that don't want to implement the
332             # cache method (which is not strictly part of the public API)
333             elsif($subclass->can($list_method))
334             {
335 0         0 foreach my $attr ($subclass->$list_method())
336             {
337 0 0       0 $Inherited_Set{$name}{$class}{'cache'}{$attr} = INHERITED_VALUE
338             unless(exists $Inherited_Set{$name}{$class}{'cache'}{$attr});
339             }
340             }
341             }
342             }
343              
344 88         200 $Inherited_Set{$name}{$class}{'meta'}{'cache_is_valid'} = 1;
345              
346 88         111 my $want = wantarray;
347              
348 88 100       174 return unless(defined $want);
349 82 50 0     358 $want ? (%{$Inherited_Set{$name}{$class}{'cache'} ||= {}}) :
  0   50     0  
350             ($Inherited_Set{$name}{$class}{'cache'} ||= {});
351 4         43 };
352              
353             $methods{$hash_method} = sub
354             {
355 8   33 8   32 my($class) = ref($_[0]) || $_[0];
356              
357 8         26 my %hash = $class->$cache_method();
358              
359 8         36 while(my($k, $v) = each %hash)
360             {
361 36 100       162 delete $hash{$k} if($v == DELETED_VALUE);
362             }
363              
364 8 50       65 return wantarray ? %hash : \%hash;
365 4         18 };
366              
367             $methods{$list_method} = sub
368             {
369 8     8   26 my($class) = shift;
370              
371 8 50       48 $class = ref $class if(ref $class);
372              
373 8 50       19 if(@_)
374             {
375 0         0 $class->$clear_method();
376 0         0 $class->$adds_method(@_);
377 0 0       0 return unless(defined wantarray);
378             }
379              
380 8         31 return wantarray ? sort keys %{$class->$hash_method()} :
  0         0  
381 8 50       21 [ sort keys %{$class->$hash_method()} ];
382 4         26 };
383              
384             $methods{$test_method} = sub
385             {
386 304 50   304   4207 my($class) = ref($_[0]) ? ref(shift) : shift;
387 304 50       754 return 0 unless(defined $_[0]);
388              
389 304 100       1382 if($Inherited_Set{$name}{$class}{'meta'}{'cache_is_valid'})
390             {
391 224 100 100     2239 return (exists $Inherited_Set{$name}{$class}{'cache'}{$_[0]} &&
392             $Inherited_Set{$name}{$class}{'cache'}{$_[0]} != DELETED_VALUE) ? 1 : 0;
393             }
394              
395 80         232 my $cache = $class->$cache_method();
396              
397 80 100 66     681 return (exists $cache->{$_[0]} && $cache->{$_[0]} != DELETED_VALUE) ? 1 : 0;
398 4         30 };
399              
400 4     58   26 $methods{$add_method} = sub { shift->$adds_method(@_) };
  58         2606  
401              
402             $methods{$adds_method} = sub
403             {
404 62 50   62   2040 my($class) = ref($_[0]) ? ref(shift) : shift;
405 62 50       139 Carp::croak("Missing value(s) to add") unless(@_);
406              
407 62         70 my $count = 0;
408              
409 62         114 foreach my $attr (@_)
410             {
411 3     3   27 no strict 'refs';
  3         15  
  3         1513  
412 70 50       163 next unless(defined $attr);
413 70         363 $Inherited_Set{$name}{$class}{'cache'}{$attr} = CLASS_VALUE;
414              
415 70 100       139 if($add_implies)
416             {
417 22         43 foreach my $method (@$add_implies)
418             {
419 22         66 $class->$method($attr);
420             }
421             }
422              
423 70         142 $count++;
424             }
425              
426             # _invalidate_inherited_set_caches($class, $name) if($count);
427             # Inlined since it is private and only called once
428 62 50       134 if($count)
429             {
430 62         80 foreach my $test_class (keys %{$Inherited_Set{$name}})
  62         225  
431             {
432 222 100 100     1317 if($test_class->isa($class) && $test_class ne $class)
433             {
434 96         346 $Inherited_Set{$name}{$test_class}{'meta'}{'cache_is_valid'} = 0;
435             }
436             }
437             }
438              
439 62         177 return $count;
440 4         18 };
441              
442             $methods{$clear_method} = sub
443             {
444 2 50   2   13 my($class) = ref($_[0]) ? ref(shift) : shift;
445 2         14 my @values = $class->$list_method();
446 2 50       11 return unless(@values);
447 2         13 $class->$deletes_method(@values);
448 4         66 };
449              
450 4     58   34 $methods{$delete_method} = sub { shift->$deletes_method(@_) };
  58         228  
451              
452             $methods{$deletes_method} = sub
453             {
454 60 50   60   181 my($class) = ref($_[0]) ? ref(shift) : shift;
455 60 50       137 Carp::croak("Missing value(s) to delete") unless(@_);
456              
457             # Init set if it doesn't exist
458 60 100       170 unless(exists $Inherited_Set{$name}{$class})
459             {
460 6         45 $class->$cache_method();
461             }
462              
463 60         68 my $count = 0;
464              
465 60         105 foreach my $attr (@_)
466             {
467 3     3   16 no strict 'refs';
  3         17  
  3         2489  
468 66 50       119 next unless(defined $attr);
469              
470 66 100 66     404 if(exists $Inherited_Set{$name}{$class}{'cache'}{$attr} &&
471             $Inherited_Set{$name}{$class}{'cache'}{$attr} != DELETED_VALUE)
472             {
473 44         94 $Inherited_Set{$name}{$class}{'cache'}{$attr} = DELETED_VALUE;
474 44         55 $count++;
475              
476 44 100       90 if($delete_implies)
477             {
478 26         44 foreach my $method (@$delete_implies)
479             {
480 26         93 $class->$method($attr);
481             }
482             }
483              
484 44         58 foreach my $test_class (keys %{$Inherited_Set{$name}})
  44         176  
485             {
486 172 100       362 next if($class eq $test_class);
487              
488 128 100 100     1314 if($test_class->isa($class) && exists $Inherited_Set{$name}{$test_class}{'cache'}{$attr} &&
      100        
489             $Inherited_Set{$name}{$test_class}{'cache'}{$attr} == INHERITED_VALUE)
490             {
491 40         83 delete $Inherited_Set{$name}{$test_class}{'cache'}{$attr};
492 40         102 $Inherited_Set{$name}{$test_class}{'meta'}{'cache_is_valid'} = 0;
493             }
494             }
495             }
496             }
497              
498             # Not required
499             #_invalidate_inherited_set_caches($class, $name) if($count);
500              
501 60         168 return $count;
502 4         33 };
503              
504 4     10   20 $methods{$inherit_method} = sub { shift->$inherits_method(@_) };
  10         68  
505              
506             $methods{$inherits_method} = sub
507             {
508 10 50   20   32 my($class) = ref($_[0]) ? ref(shift) : shift;
509 10 50       28 Carp::croak("Missing value(s) to inherit") unless(@_);
510              
511 10         14 my $count = 0;
512              
513 10         23 foreach my $attr (@_)
514             {
515 10 100 66     87 if(exists $Inherited_Set{$name}{$class}{'cache'}{$attr} &&
516             $Inherited_Set{$name}{$class}{'cache'}{$attr} == DELETED_VALUE)
517             {
518 8         43 delete $Inherited_Set{$name}{$class}{'cache'}{$attr};
519 8         25 $Inherited_Set{$name}{$class}{'meta'}{'cache_is_valid'} = 0;
520 8         16 $count++;
521             }
522              
523 10 100       38 if($inherit_implies)
524             {
525 4         12 foreach my $method (@$inherit_implies)
526             {
527 4         30 $class->$method($attr);
528             }
529             }
530             }
531              
532 10         30 return $count;
533 4         27 };
534              
535 4 50       12 if($interface ne 'all')
536             {
537 0         0 Carp::croak "Unknown interface: $interface";
538             }
539              
540 4         76 return \%methods;
541             }
542              
543             # Inlined above since it is private and only called once
544             # sub _invalidate_inherited_set_caches
545             # {
546             # my($class, $name) = @_;
547             #
548             # foreach my $test_class (keys %{$Inherited_Set{$name}})
549             # {
550             # if($test_class->isa($class) && $test_class ne $class)
551             # {
552             # $Inherited_Set{$name}{$test_class}{'meta'}{'cache_is_valid'} = 0;
553             # }
554             # }
555             # }
556              
557             1;
558              
559             __END__