File Coverage

blib/lib/Class/MakeMethods/Composite/Inheritable.pm
Criterion Covered Total %
statement 30 32 93.7
branch n/a
condition n/a
subroutine 12 14 85.7
pod 5 6 83.3
total 47 52 90.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Class::MakeMethods::Composite::Inheritable - Overridable data
4              
5             =head1 SYNOPSIS
6              
7             package MyClass;
8              
9             use Class::MakeMethods( 'Composite::Inheritable:scalar' => 'foo' );
10             # We now have an accessor method for an "inheritable" scalar value
11            
12             MyClass->foo( 'Foozle' ); # Set a class-wide value
13             print MyClass->foo(); # Retrieve class-wide value
14            
15             my $obj = MyClass->new(...);
16             print $obj->foo(); # All instances "inherit" that value...
17            
18             $obj->foo( 'Foible' ); # until you set a value for an instance.
19             print $obj->foo(); # This now finds object-specific value.
20             ...
21            
22             package MySubClass;
23             @ISA = 'MyClass';
24            
25             print MySubClass->foo(); # Intially same as superclass,
26             MySubClass->foo('Foobar'); # but overridable per subclass,
27             print $subclass_obj->foo(); # and shared by its instances
28             $subclass_obj->foo('Fosil');# until you override them...
29             ...
30            
31             # Similar behaviour for hashes and arrays is currently incomplete
32             package MyClass;
33             use Class::MakeMethods::Composite::Inheritable (
34             array => 'my_list',
35             hash => 'my_index',
36             );
37            
38             MyClass->my_list(0 => 'Foozle', 1 => 'Bang!');
39             print MyClass->my_list(1);
40            
41             MyClass->my_index('broccoli' => 'Blah!', 'foo' => 'Fiddle');
42             print MyClass->my_index('foo');
43              
44              
45             =head1 DESCRIPTION
46              
47             The MakeMethods subclass provides accessor methods that search an inheritance tree to find a value. This allows you to set a shared or default value for a given class, optionally override it in a subclass, and then optionally override it on a per-instance basis.
48              
49             Note that all MakeMethods methods are inheritable, in the sense that they work as expected for subclasses. These methods are different in that the I accessed by each method can be inherited or overridden in each subclass or instance. See L< Class::MakeMethods::Utility::Inheritable> for more about this type of "inheritable" or overridable" data.
50              
51              
52             =head2 Class::MakeMethods Calling Interface
53              
54             When you C this package, the method declarations you provide
55             as arguments cause subroutines to be generated and installed in
56             your module.
57              
58             See L for more information.
59              
60             =head2 Class::MakeMethods::Standard Declaration Syntax
61              
62             To declare methods, pass in pairs of a method-type name followed
63             by one or more method names.
64              
65             See the "METHOD GENERATOR TYPES" section below for a list of the supported values of I.
66              
67             See L and L for more information.
68              
69             =cut
70              
71             package Class::MakeMethods::Composite::Inheritable;
72              
73             $VERSION = 1.000;
74 3     3   25524 use strict;
  3         7  
  3         275  
75 3     3   19 use Carp;
  3         5  
  3         1204  
76              
77 3     3   5842 use Class::MakeMethods::Composite '-isasubclass';
  3         8  
  3         28  
78 3     3   2115 use Class::MakeMethods::Utility::Inheritable qw(get_vvalue set_vvalue find_vself );
  3         8  
  3         19  
79              
80             ########################################################################
81              
82             =head1 METHOD GENERATOR TYPES
83              
84             =head2 scalar - Overrideable Accessor
85              
86             For each method name passed, uses a closure to generate a subroutine with the following characteristics:
87              
88             =over 4
89              
90             =item *
91              
92             May be called as a class or instance method, on the declaring class or any subclass.
93              
94             =item *
95              
96             If called without any arguments returns the current value for the callee. If the callee has not had a value defined for this method, searches up from instance to class, and from class to superclass, until a callee with a value is located.
97              
98             =item *
99              
100             If called with an argument, stores that as the value associated with the callee, whether instance or class, and returns it,
101              
102             =item *
103              
104             If called with multiple arguments, stores a reference to a new array with those arguments as contents, and returns that array reference.
105              
106             =back
107              
108             Sample declaration and usage:
109              
110             package MyClass;
111             use Class::MakeMethods::Composite::Inheritable (
112             scalar => 'foo',
113             );
114             ...
115            
116             # Store value
117             MyClass->foo('Foozle');
118            
119             # Retrieve value
120             print MyClass->foo;
121              
122             =cut
123              
124 3     3   14 use vars qw( %ScalarFragments );
  3         4  
  3         20705  
125              
126             sub scalar {
127 2     2 1 11 (shift)->_build_composite( \%ScalarFragments, @_ );
128             }
129              
130             %ScalarFragments = (
131             '' => [
132             '+init' => sub {
133             my ($method) = @_;
134             $method->{target_class} ||= $Class::MethodMaker::CONTEXT{TargetClass};
135             $method->{data} ||= {};
136             },
137             'do' => sub {
138             my $method = pop @_;
139             my $self = shift @_;
140             if ( scalar(@_) == 0 ) {
141             return get_vvalue($method->{data}, $self);
142             } else {
143             my $value = (@_ == 1 ? $_[0] : [@_]);
144             set_vvalue($method->{data}, $self, $value);
145             }
146             },
147             ],
148             'rw' => [],
149             'p' => [
150             '+pre' => sub {
151             my $method = pop @_;
152             my $self = shift @_;
153             unless ( UNIVERSAL::isa((caller(1))[0], $method->{target_class}) ) {
154             croak "Method $method->{name} is protected";
155             }
156             },
157             ],
158             'pp' => [
159             '+pre' => sub {
160             my $method = pop @_;
161             my $self = shift @_;
162             unless ( (caller(1))[0] eq $method->{target_class} ) {
163             croak "Method $method->{name} is private";
164             }
165             },
166             ],
167             'pw' => [
168             '+pre' => sub {
169             my $method = pop @_;
170             my $self = shift @_;
171             unless ( @_ == 0 or UNIVERSAL::isa((caller(1))[0], $method->{target_class}) ) {
172             croak "Method $method->{name} is write-protected";
173             }
174             },
175             ],
176             'ppw' => [
177             '+pre' => sub {
178             my $method = pop @_;
179             my $self = shift @_;
180             unless ( @_ == 0 or (caller(1))[0] eq $method->{target_class} ) {
181             croak "Method $method->{name} is write-private";
182             }
183             },
184             ],
185             'r' => [
186             '+pre' => sub {
187             my $method = pop @_;
188             my $self = shift @_;
189             @{ $method->{args} } = ();
190             },
191             ],
192             'ro' => [
193             '+pre' => sub {
194             my $method = pop @_;
195             my $self = shift @_;
196             unless ( @_ == 0 ) {
197             croak("Method $method->{name} is read-only");
198             }
199             },
200             ],
201             'wo' => [
202             '+pre' => sub {
203             my $method = pop @_;
204             my $self = shift @_;
205             if ( @_ == 0 ) {
206             croak("Method $method->{name} is write-only");
207             }
208             },
209             ],
210             'return_original' => [
211             '+pre' => sub {
212             my $method = pop @_;
213             my $self = shift @_;
214             my $v_self = find_vself($method->{data}, $self);
215             $method->{scratch}{return_original} =
216             $v_self ? $method->{data}{$v_self} : ();
217             },
218             '+post' => sub {
219             my $method = pop @_;
220             $method->{result} = \{ $method->{scratch}{return_original} };
221             },
222             ],
223             );
224              
225             ########################################################################
226              
227             =head2 array - Overrideable Ref Accessor
228              
229             For each method name passed, uses a closure to generate a subroutine with the following characteristics:
230              
231             =over 4
232              
233             =item *
234              
235             May be called as a class method, or on any instance or subclass, Must be called on a hash-based instance.
236              
237             =item *
238              
239             The class value will be a reference to an array (or undef).
240              
241             =item *
242              
243             If called without any arguments, returns the current array-ref value (or undef).
244              
245              
246             =item *
247              
248             If called with a single non-ref argument, uses that argument as an index to retrieve from the referenced array, and returns that value (or undef).
249              
250             =item *
251              
252             If called with a single array ref argument, uses that list to return a slice of the referenced array.
253              
254             =item *
255              
256             If called with a list of argument pairs, each with a non-ref index and an associated value, stores the value at the given index in the referenced array. If the class value was previously undefined, a new array is autovivified. The current value in each position will be overwritten, and later arguments with the same index will override earlier ones. Returns the current array-ref value.
257              
258             =item *
259              
260             If called with a list of argument pairs, each with the first item being a reference to an array of up to two numbers, loops over each pair and uses those numbers to splice the value array.
261              
262             The first controlling number is the position at which the splice will begin. Zero will start before the first item in the list. Negative numbers count backwards from the end of the array.
263              
264             The second number is the number of items to be removed from the list. If it is omitted, or undefined, or zero, no items are removed. If it is a positive integer, that many items will be returned.
265              
266             If both numbers are omitted, or are both undefined, they default to containing the entire value array.
267              
268             If the second argument is undef, no values will be inserted; if it is a non-reference value, that one value will be inserted; if it is an array-ref, its values will be copied.
269              
270             The method returns the items that removed from the array, if any.
271              
272             =back
273              
274             Sample declaration and usage:
275            
276             package MyClass;
277             use Class::MakeMethods::Composite::Inheritable (
278             array => 'bar',
279             );
280             ...
281            
282             # Clear and set contents of list
283             print MyClass->bar([ 'Spume', 'Frost' ] );
284            
285             # Set values by position
286             MyClass->bar(0 => 'Foozle', 1 => 'Bang!');
287            
288             # Positions may be overwritten, and in any order
289             MyClass->bar(2 => 'And Mash', 1 => 'Blah!');
290            
291             # Retrieve value by position
292             print MyClass->bar(1);
293            
294             # Direct access to referenced array
295             print scalar @{ MyClass->bar() };
296              
297             There are also calling conventions for slice and splice operations:
298              
299             # Retrieve slice of values by position
300             print join(', ', MyClass->bar( undef, [0, 2] ) );
301            
302             # Insert an item at position in the array
303             MyClass->bar([3], 'Potatoes' );
304            
305             # Remove 1 item from position 3 in the array
306             MyClass->bar([3, 1], undef );
307            
308             # Set a new value at position 2, and return the old value
309             print MyClass->bar([2, 1], 'Froth' );
310              
311             B
312              
313             =cut
314              
315 3     3   306 use vars qw( %ArrayFragments );
  3         5  
  3         2493  
316              
317             sub array {
318 0     0 1 0 (shift)->_build_composite( \%ArrayFragments, @_ );
319             }
320              
321             %ArrayFragments = (
322             '' => [
323             '+init' => sub {
324             my ($method) = @_;
325             $method->{hash_key} ||= $_->{name};
326             $method->{data} ||= {};
327             },
328             'do' => sub {
329             my $method = pop @_;
330             my $self = shift @_;
331            
332             if ( scalar(@_) == 0 ) {
333             my $v_self = find_vself($method->{data}, $self);
334             my $value = $v_self ? $method->{data}{$v_self} : ();
335             if ( $method->{auto_init} and ! $value ) {
336             $value = $method->{data}{$self} = [];
337             }
338             ( ! $value ) ? () : wantarray ? @$value : $value;
339            
340             } elsif ( scalar(@_) == 1 and ref $_[0] eq 'ARRAY' ) {
341             $method->{data}{$self} = [ @{ $_[0] } ];
342             wantarray ? @{ $method->{data}{$self} } : $method->{data}{$self}
343            
344             } else {
345             if ( ! exists $method->{data}{$self} ) {
346             my $v_self = find_vself($method->{data}, $self);
347             $method->{data}{$self} = [ $v_self ? @{$method->{data}{$v_self}} : () ];
348             }
349             return array_splicer( $method->{data}{$self}, @_ );
350             }
351             },
352             ],
353             );
354              
355             ########################################################################
356              
357             =head2 hash - Overrideable Ref Accessor
358              
359             For each method name passed, uses a closure to generate a subroutine with the following characteristics:
360              
361             =over 4
362              
363             =item *
364              
365             May be called as a class method, or on any instance or subclass, Must be called on a hash-based instance.
366              
367             =item *
368              
369             The class value will be a reference to a hash (or undef).
370              
371             =item *
372              
373             If called without any arguments returns the contents of the hash in list context, or a hash reference in scalar context for the callee. If the callee has not had a value defined for this method, searches up from instance to class, and from class to superclass, until a callee with a value is located.
374              
375             =item *
376              
377             If called with one non-ref argument, uses that argument as an index to retrieve from the referenced hash, and returns that value (or undef). If the callee has not had a value defined for this method, searches up from instance to class, and from class to superclass, until a callee with a value is located.
378              
379             =item *
380              
381             If called with one array-ref argument, uses the contents of that array to retrieve a slice of the referenced hash. If the callee has not had a value defined for this method, searches up from instance to class, and from class to superclass, until a callee with a value is located.
382              
383             =item *
384              
385             If called with one hash-ref argument, sets the contents of the referenced hash to match that provided.
386              
387             =item *
388              
389             If called with a list of key-value pairs, stores the value under the given key in the hash associated with the callee, whether instance or class. If the callee did not previously have a hash-ref value associated with it, searches up instance to class, and from class to superclass, until a callee with a value is located, and copies that hash before making the assignments. The current value under each key will be overwritten, and later arguments with the same key will override earlier ones. Returns the contents of the hash in list context, or a hash reference in scalar context.
390              
391             =back
392              
393             Sample declaration and usage:
394              
395             package MyClass;
396             use Class::MakeMethods::Composite::Inheritable (
397             hash => 'baz',
398             );
399             ...
400            
401             # Set values by key
402             MyClass->baz('foo' => 'Foozle', 'bar' => 'Bang!');
403            
404             # Values may be overwritten, and in any order
405             MyClass->baz('broccoli' => 'Blah!', 'foo' => 'Fiddle');
406            
407             # Retrieve value by key
408             print MyClass->baz('foo');
409            
410             # Retrive slice of values by position
411             print join(', ', MyClass->baz( ['foo', 'bar'] ) );
412            
413             # Direct access to referenced hash
414             print keys %{ MyClass->baz() };
415            
416             # Reset the hash contents to empty
417             @{ MyClass->baz() } = ();
418              
419             B
420              
421             =cut
422              
423 3     3   139 use vars qw( %HashFragments );
  3         71  
  3         6639  
424              
425             sub hash {
426 1     1 1 163 (shift)->_build_composite( \%HashFragments, @_ );
427             }
428              
429             %HashFragments = (
430             '' => [
431             '+init' => sub {
432             my ($method) = @_;
433             $method->{hash_key} ||= $_->{name};
434             $method->{data} ||= {};
435             },
436             'do' => sub {
437             my $method = pop @_;
438             my $self = shift @_;
439            
440             if ( scalar(@_) == 0 ) {
441             my $value = get_vvalue($method->{data}, $self);
442             if ( $method->{auto_init} and ! $value ) {
443             $value = set_vvalue( $method->{data}, $self, {} );
444             }
445             wantarray ? %$value : $value;
446             } elsif ( scalar(@_) == 1 ) {
447             if ( ref($_[0]) eq 'HASH' ) {
448             %{$method->{data}{$self}} = %{$_[0]};
449             } elsif ( ref($_[0]) eq 'ARRAY' ) {
450             my $v_self = find_vself($method->{data}, $self) or return;
451             return @{ $method->{data}{$v_self} }{ @{$_[0]} }
452             } else {
453             my $v_self = find_vself($method->{data}, $self) or return;
454             return $method->{data}{$v_self}{ $_[0] }
455             }
456              
457             } elsif ( scalar(@_) % 2 ) {
458             Carp::croak "Odd number of items in assigment to $method->{name}";
459             } else {
460             if ( ! exists $method->{data}{$self} ) {
461             my $v_self = find_vself($method->{data}, $self);
462             $method->{data}{$self} = { $v_self ? %{ $method->{data}{$v_self} } : () };
463             }
464             while ( scalar(@_) ) {
465             my $key = shift();
466             $method->{data}{$self}->{ $key } = shift();
467             }
468             wantarray ? %{$method->{data}{$self}} : $method->{data}{$self};
469             }
470             },
471             ],
472             );
473              
474             ########################################################################
475              
476             =head2 hook - Overrideable array of subroutines
477              
478             A hook method is called from the outside as a normal method. However, internally, it contains an array of subroutine references, each of which are called in turn to produce the method's results.
479              
480             Subroutines may be added to the hook's array by calling it with a blessed subroutine reference, as shown below. Subroutines may be added on a class-wide basis or on an individual object.
481              
482             You might want to use this type of method to provide an easy way for callbacks to be registered.
483              
484             package MyClass;
485             use Class::MakeMethods::Composite::Inheritable ( 'hook' => 'init' );
486            
487             MyClass->init( Class::MakeMethods::Composite::Inheritable->Hook( sub {
488             my $callee = shift;
489             warn "Init...";
490             } );
491            
492             my $obj = MyClass->new;
493             $obj->init();
494              
495             =cut
496              
497 3     3   507 use vars qw( %HookFragments );
  3         7  
  3         5185  
498              
499             sub hook {
500 1     1 1 9 (shift)->_build_composite( \%HookFragments, @_ );
501             }
502              
503             %HookFragments = (
504             '' => [
505             '+init' => sub {
506             my ($method) = @_;
507             $method->{data} ||= {};
508             },
509             'do' => sub {
510             my $method = pop @_;
511             my $self = shift @_;
512            
513             if ( scalar(@_) and
514             ref($_[0]) eq 'Class::MakeMethods::Composite::Inheritable::Hook' ) {
515             if ( ! exists $method->{data}{$self} ) {
516             my $v_self = find_vself($method->{data}, $self);
517             $method->{data}{$self} = [ $v_self ? @{ $method->{data}{$v_self} } : () ];
518             }
519             push @{ $method->{data}{$self} }, map $$_, @_;
520             } else {
521             my $v_self = find_vself($method->{data}, $self);
522             my $subs = $v_self ? $method->{data}{$v_self} : ();
523             my @subs = ( ( ! $subs ) ? () : @$subs );
524            
525             if ( ! defined $method->{wantarray} ) {
526             foreach my $sub ( @subs ) {
527             &$sub( @{$method->{args}} );
528             }
529             } elsif ( ! $method->{wantarray} ) {
530             foreach my $sub ( @subs ) {
531             my $value = &$sub( @{$method->{args}} );
532             if ( defined $value ) {
533             $method->{result} = \$value;
534             }
535             }
536             } else {
537             foreach my $sub ( @subs ) {
538             my @value = &$sub( @{$method->{args}} );
539             if ( scalar @value ) {
540             push @{ $method->{result} }, @value;
541             }
542             }
543             }
544            
545             }
546             return Class::MakeMethods::Composite->CurrentResults();
547             },
548             ],
549             );
550              
551             sub Hook (&) {
552 2     2 0 5 my $package = shift;
553 2         4 my $sub = shift;
554 2         24 bless \$sub, 'Class::MakeMethods::Composite::Inheritable::Hook';
555             }
556              
557             ########################################################################
558              
559             =head2 object - Overrideable Ref Accessor
560              
561             For each method name passed, uses a closure to generate a subroutine with the following characteristics:
562              
563             =over 4
564              
565             =item *
566              
567             May be called as a class method, or on any instance or subclass, Must be called on a hash-based instance.
568              
569             =item *
570              
571             The class value will be a reference to an object (or undef).
572              
573             =item *
574              
575             If called without any arguments returns the current value for the callee. If the callee has not had a value defined for this method, searches up from instance to class, and from class to superclass, until a callee with a value is located.
576              
577             =item *
578              
579             If called with an argument, stores that as the value associated with the callee, whether instance or class, and returns it,
580              
581             =back
582              
583             Sample declaration and usage:
584              
585             package MyClass;
586             use Class::MakeMethods::Composite::Inheritable (
587             object => 'foo',
588             );
589             ...
590            
591             # Store value
592             MyClass->foo( Foozle->new() );
593            
594             # Retrieve value
595             print MyClass->foo;
596              
597             B
598              
599             =cut
600              
601 0     0 1   sub object { }
602              
603             ########################################################################
604              
605             =head1 SEE ALSO
606              
607             See L for general information about this distribution.
608              
609             See L for more about this family of subclasses.
610              
611             =cut
612              
613             1;