File Coverage

blib/lib/Class/MakeMethods/Composite.pm
Criterion Covered Total %
statement 79 87 90.8
branch 31 42 73.8
condition n/a
subroutine 9 10 90.0
pod 0 2 0.0
total 119 141 84.4


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Class::MakeMethods::Composite - Make extensible compound methods
4              
5              
6             =head1 SYNOPSIS
7              
8             package MyObject;
9             use Class::MakeMethods::Composite::Hash (
10             new => 'new',
11             scalar => [ 'foo', 'bar' ],
12             array => 'my_list',
13             hash => 'my_index',
14             );
15              
16              
17             =head1 DESCRIPTION
18              
19             This document describes the various subclasses of Class::MakeMethods
20             included under the Composite::* namespace, and the method types each
21             one provides.
22              
23             The Composite subclasses provide a parameterized set of method-generation
24             implementations.
25              
26             Subroutines are generated as closures bound to a hash containing
27             the method name and additional parameters, including the arrays of subroutine references that will provide the method's functionality.
28              
29              
30             =head2 Calling Conventions
31              
32             When you C this package, the method names you provide
33             as arguments cause subroutines to be generated and installed in
34             your module.
35              
36             See L for more information.
37              
38             =head2 Declaration Syntax
39              
40             To declare methods, pass in pairs of a method-type name followed
41             by one or more method names.
42              
43             Valid method-type names for this package are listed in L<"METHOD
44             GENERATOR TYPES">.
45              
46             See L and L for more information.
47              
48             =cut
49              
50             package Class::MakeMethods::Composite;
51              
52             $VERSION = 1.000;
53 7     7   38 use strict;
  7         13  
  7         266  
54 7     7   20213 use Class::MakeMethods '-isasubclass';
  7         18  
  7         46  
55 7     7   43 use Carp;
  7         11  
  7         673  
56              
57             ########################################################################
58              
59             =head2 About Composite Methods
60              
61             The methods generated by Class::MakeMethods::Composite are assembled
62             from groups of "fragment" subroutines, each of which provides some
63             aspect of the method's behavior.
64              
65             You can add pre- and post- operations to any composite method.
66              
67             package MyObject;
68             use Class::MakeMethods::Composite::Hash (
69             new => 'new',
70             scalar => [
71             'foo' => {
72             'pre_rules' => [
73             sub {
74             # Don't automatically convert list to array-ref
75             croak "Too many arguments" if ( scalar @_ > 2 );
76             }
77             ],
78             'post_rules' => [
79             sub {
80             # Don't let anyone see my credit card number!
81             ${(pop)->{result}} =~ s/\d{13,16}/****/g;
82             }
83             ],
84             }
85             ],
86             );
87              
88             =cut
89              
90 7     7   33 use vars qw( $Method );
  7         12  
  7         8364  
91              
92             sub CurrentMethod {
93 0     0 0 0 $Method;
94             }
95              
96             sub CurrentResults {
97 8     8 0 19 my $package = shift;
98 8 100       21 if ( ! scalar @_ ) {
    50          
99 0         0 ( ! $Method->{result} ) ? () :
100 4         12 ( ref($Method->{result}) eq 'ARRAY' ) ? @{$Method->{result}} :
101 7 50       31 ${$Method->{result}};
    100          
102             } elsif ( scalar @_ == 1) {
103 1         3 my $value = shift;
104 1         4 $Method->{result} = \$value;
105 1         3 $value
106             } else {
107 0         0 my @value = @_;
108 0         0 $Method->{result} = \@value;
109 0         0 @value;
110             }
111             }
112              
113             sub _build_composite {
114 29     29   55 my $class = shift;
115 29         50 my $fragments = shift;
116 52         69 map {
117 29         197 my $method = $_;
118 52         57 my @fragments = @{ $fragments->{''} };
  52         144  
119 52         187 foreach my $flagname ( grep $method->{$_}, qw/ permit modifier / ) {
120 12         13 my $value = $method->{$flagname};
121 12 50       27 my $fragment = $fragments->{$value}
122             or croak "Unsupported $flagname flag '$value'";
123 12         31 push @fragments, @$fragment;
124             }
125 52         121 _bind_composite( $method, @fragments );
126             } $class->_get_declarations(@_)
127             }
128              
129             sub _assemble_fragments {
130 52     52   60 my $method = shift;
131 52         101 my @fragments = @_;
132 52         118 while ( scalar @fragments ) {
133 116         195 my ($rule, $sub) = splice( @fragments, 0, 2 );
134 116 100       1124 if ( $rule =~ s/\A\+// ) {
    50          
    50          
135 60         81 unshift @{$method->{"${rule}_rules"}}, $sub
  60         276  
136             } elsif ( $rule =~ s/\+\Z// ) {
137 0         0 push @{$method->{"${rule}_rules"}}, $sub
  0         0  
138             } elsif ( $rule =~ /\A\w+\Z/ ) {
139 56         66 @{$method->{"${rule}_rules"}} = $sub;
  56         271  
140             } else {
141 0         0 croak "Unsupported rule type '$rule'"
142             }
143             }
144             }
145              
146             sub _bind_composite {
147 52     52   64 my $method = shift;
148 52         114 _assemble_fragments( $method, @_ );
149 52 50       136 if ( my $subs = $method->{"init_rules"} ) {
150 52         81 foreach my $sub ( @$subs ) {
151 52         136 &$sub( $method );
152             }
153             }
154             $method->{name} => sub {
155 112     112   12324 local $Method = $method;
156 112         364 local $Method->{args} = [ @_ ];
157 112         259 local $Method->{result};
158 112         204 local $Method->{scratch};
159             # Strange but true: you can local a hash-value in hash that's not
160             # a package variable. Confirmed in in 5.004, 5.005, 5.6.0.
161              
162 112         229 local $Method->{wantarray} = wantarray;
163              
164 112 100       292 if ( my $subs = $Method->{"pre_rules"} ) {
165 9         16 foreach my $sub ( @$subs ) {
166 14         31 &$sub( @{$Method->{args}}, $Method );
  14         45  
167             }
168             }
169            
170 110 50       274 my $subs = $Method->{"do_rules"}
171             or Carp::confess("No operations provided for $Method->{name}");
172 110 100       303 if ( ! defined $Method->{wantarray} ) {
    100          
173 24         41 foreach my $sub ( @$subs ) {
174 24 50       69 last if $Method->{result};
175 24         29 &$sub( @{$Method->{args}}, $Method );
  24         95  
176             }
177             } elsif ( ! $Method->{wantarray} ) {
178 83         136 foreach my $sub ( @$subs ) {
179 83 50       179 last if $Method->{result};
180 83         81 my $value = &$sub( @{$Method->{args}}, $Method );
  83         368  
181 83 100       234 if ( defined $value ) {
182 73         229 $Method->{result} = \$value;
183             }
184             }
185             } else {
186 3         12 foreach my $sub ( @$subs ) {
187 3 50       8 last if $Method->{result};
188 3         5 my @value = &$sub( @{$Method->{args}}, $Method );
  3         9  
189 3 50       8 if ( scalar @value ) {
190 3         20 $Method->{result} = \@value;
191             }
192             }
193             }
194            
195 110 100       284 if ( my $subs = $Method->{"post_rules"} ) {
196 5         8 foreach my $sub ( @$subs ) {
197 5         4 &$sub( @{$Method->{args}}, $Method );
  5         13  
198             }
199             }
200            
201 3         15 ( ! $Method->{result} ) ? () :
202 73         500 ( ref($Method->{result}) eq 'ARRAY' ) ? @{$Method->{result}} :
203 110 100       572 ${$Method->{result}};
    100          
204             }
205 52         539 }
206              
207             ########################################################################
208              
209             =head1 SEE ALSO
210              
211             See L for general information about this distribution.
212              
213             For distribution, installation, support, copyright and license
214             information, see L.
215              
216             =cut
217              
218             1;