File Coverage

lib/AutoCode/AccessorMaker.pm
Criterion Covered Total %
statement 260 273 95.2
branch 66 86 76.7
condition 8 17 47.0
subroutine 41 44 93.1
pod 0 5 0.0
total 375 425 88.2


line stmt bran cond sub pod time code
1             ###
2             # POD documents at the end of the file
3             ###
4             package AutoCode::AccessorMaker;
5 7     7   31 use strict;
  7         11  
  7         292  
6             our $VERSION='0.01';
7 7     7   34 use AutoCode::Root0;
  7         11  
  7         35  
8             our @ISA=qw(AutoCode::Root0);
9             # use AutoCode::Initializer;
10 7     7   30 use AutoCode::SymbolTableUtils;
  7         13  
  7         4146  
11             our %AUTO_ACCESSORS;
12              
13             our $VALID_ACCESSOR_NAME='[_a-z][_a-z0-9]+';
14             $VALID_ACCESSOR_NAME='[_a-zA-Z][_a-zA-Z0-9]+';
15              
16             sub import {
17 34     34   188 my ($class, @args)=@_;
18 34         151 my $self = $class->new;
19 34   33     171 my $caller = ref(caller) || caller;
20 34         90 my %args=@args;
21            
22 34 100       128 if(exists $args{'$'}){
23 27         97 foreach ($class->_scalar_to_array($args{'$'})){
24 79         180 $self->make_scalar_accessor($_, $caller);
25             }
26             }
27              
28 34 100       104 if(exists $args{'@'}){
29 17         52 foreach($class->_scalar_to_array($args{'@'})){
30 52         130 $self->make_array_accessor($_, $caller);
31             }
32             }
33              
34 34 100       101 if(exists $args{'%'}){
35 1         3 foreach($class->_scalar_to_array($args{'%'})){
36 1         2 $self->make_hash_accessor($_, $caller);
37             }
38             }
39              
40 34 100 66     3116 if(exists $args{_initialize} or exists $args{_digest_args}){
41 6         16 my $initializer=$self;
42            
43 6 50       26 if(exists $args{_initialize}){
44 6         42 $initializer->_make_initialize_by_hash(\%args, $caller);
45             }
46 6 50       211 if(exists $args{_digest_args}){
47 0         0 $initializer->_make_digest_args_by_hash(\%args, $caller);
48             }
49             }
50             }
51              
52             sub _scalar_to_array {
53 45     45   66 my ($class, $scalar)=@_;
54 45         78 my $ref=ref($scalar);
55 45 100       114 if($ref eq'ARRAY'){
    50          
56 44         141 return @$scalar;
57             }elsif($ref eq ''){
58 1         2 return ($scalar);
59             }else{
60 0         0 $class->throw("ref [$ref] is neither nothing nor ARRAY");
61             }
62             }
63              
64             # This method is only invoked by make_scalar_accessor and make_array_accessor
65             # While subroutine defined the argument of those two method abovementioned.
66             # This most hacky part is caller(2); that mean the first immedicate package
67             # after this Module.
68             sub __accessor_to_glob {
69 193     193   276 my ($self, $accessor, $pkg)=@_;
70 193 50       358 defined $accessor or $self->throw("method_name needed as 2nd arg");
71 193 100       376 my $singular = (ref($accessor) eq 'ARRAY')? $accessor->[0]: $accessor;
72             # According to the specification of AutoCode, upper letter are not allowed
73             # in the names of methods which are automatically generated by this system.
74 193 50       1231 $self->throw("'$singular' method name must match /^$VALID_ACCESSOR_NAME\$/")
75             unless $singular =~ /^$VALID_ACCESSOR_NAME$/;
76 193         182 if(0){ # For debug
77             print "$_\t". (caller($_))[0]."\n" foreach(0..3);
78             $self->throw("");
79             }
80 193   33     347 $pkg ||= (caller(2))[0]; # This line will definitely assign a value.
81              
82             # This typeglob is meaningful for both scalar and array accessors.
83             # For scalar, it means the same as the real typeglob;
84             # for array, there is no such method with exact method, but a symbol for
85             # these three methods.
86 193         324 my $typeglob="$pkg\::$singular";
87 193 100       835 unless(grep {$_ eq $typeglob} keys %AUTO_ACCESSORS){
  3343         4642  
88             # push @{$self->{AUTO_ACCESSORS_SLOT}}, $typeglob;
89 177         455 $AUTO_ACCESSORS{$typeglob}=1;
90             }
91 193         536 my $slot="$pkg\::_auto_accessors::$singular";
92 193         585 return ($accessor, $pkg, $typeglob, $slot);
93            
94             }
95              
96             sub make_scalar_accessor {
97 118     118 0 132 my $self=shift;
98 118         255 my ($accessor, $pkg, $typeglob, $slot) = $self->__accessor_to_glob(@_);
99            
100 118         205 $typeglob="$pkg\::$accessor";
101 118         203 $slot="$pkg\::$accessor\_\$";
102 118         472 $self->debug("making a scalar accessor [$typeglob]");
103              
104 118 100       271 return if(AutoCode::SymbolTableUtils::CODE_exists_in_ST($typeglob));
105 7     7   42 no strict 'refs';
  7         10  
  7         2095  
106 108 50       427 return if defined &$typeglob;
107             *$typeglob =sub{
108 446     446   1039 my $self=shift;
109 446 100       1095 $self->{$slot}=shift if @_;
110 446         1755 return $self->{$slot};
111 108         827 };
112             }
113              
114             sub _deref_plural {
115 150     150   196 my ($self, $accessor)=@_;
116 150 100       528 (ref($accessor) eq 'ARRAY')? @$accessor: ($accessor, "${accessor}s");
117             }
118              
119             sub make_array_accessor {
120 66     66 0 82 my $self=shift;
121 66         128 my ($accessor, $pkg, $typeglob, $slot)=$self->__accessor_to_glob(@_);
122 66         174 my ($singular, $plural) = $self->_deref_plural($accessor);
123             # $typeglob is useless here. So the 3 new method globs are composed here
124 66         119 my $add_method="$pkg\::add_$singular";
125 66         117 my $get_method="$pkg\::get_$plural";
126 66         92 my $remove_method="$pkg\::remove_$plural";
127 66         109 foreach $typeglob($add_method, $get_method, $remove_method){
128 190 100       440 return if(AutoCode::SymbolTableUtils::CODE_exists_in_ST($typeglob));
129             }
130 62         129 $slot="$pkg\::$singular\_\%";
131 62         139 $self->_make_array_add($add_method, $slot);
132 62         153 $self->_make_array_get($get_method, $slot);
133 62         155 $self->_make_array_remove($remove_method, $slot, $get_method);
134             }
135              
136             sub _make_array_add {
137 62     62   107 my ($self, $glob, $slot)=@_;
138 7     7   36 no strict 'refs';
  7         10  
  7         842  
139             *$glob=sub{
140 120 50   120   174 my $self=shift; return unless @_;
  120         242  
141            
142 120         192 foreach my $value(@_){
143             # Avoid duplicates
144 120 50       121 next if grep /^$value$/, @{$self->{$slot}};
  120         936  
145 120         148 push @{$self->{$slot}}, $value;
  120         1057  
146             }
147 62         450 };
148             }
149              
150             sub _make_array_get {
151 62     62   102 my ($self, $glob, $slot)=@_;
152 7     7   29 no strict 'refs';
  7         10  
  7         570  
153             *$glob=sub{
154 201     201   302 my $self=shift;
155 201 100       507 return @{$self->{$slot}} if exists $self->{$slot};
  150         1025  
156 51         160 return ();
157 62         376 };
158             }
159              
160             sub _make_array_remove {
161 62     62   118 my ($self, $glob, $slot, $get_method)=@_;
162 7     7   30 no strict 'refs';
  7         11  
  7         1112  
163             *$glob=sub{
164 13     13   65 my $self=shift;
165 13         17 my @olds=&{$get_method}($self);
  13         54  
166 13         50 $self->{$slot}=[];
167 13         194 return @olds;
168 62         542 };
169             }
170              
171             sub make_hash_accessor {
172 9     9 0 16 my $self=shift;
173 9         34 my ($accessor, $pkg, $typeglob, $slot)=$self->__accessor_to_glob(@_);
174 9         33 my ($singular, $plural) =$self->_deref_plural($accessor);
175            
176 9         27 $slot="$pkg\::$singular\_\%";
177 9         24 my $add_method="$pkg\::add_$singular";
178 9         17 my $get_method="$pkg\::get_$plural";
179 9         23 my $remove_method="$pkg\::remove_$plural";
180             {
181 7     7   35 no strict 'refs';
  7         11  
  7         735  
  9         15  
182 9         17 foreach $typeglob($add_method, $get_method, $remove_method){
183 23 100       130 return if defined &$typeglob;
184             }
185             }
186              
187 7         33 $self->_make_hash_add($add_method, $slot);
188 7         26 $self->_make_hash_get($get_method, $slot);
189 7         28 $self->_make_hash_remove($remove_method, $slot, $get_method);
190             }
191              
192             sub _make_hash_add {
193 7     7   16 my ($self, $glob, $slot)=@_;
194 7     7   34 no strict 'refs';
  7         14  
  7         726  
195             *$glob=sub{
196 3 50   3   4 my $self=shift; return unless @_;
  3         7  
197 3 100       10 $self->{$slot}={} unless exists $self->{$slot};
198 3         2 my $key=shift;
199 3         7 $self->{$slot}->{$key}=shift;
200 3         6 1;
201 7         58 };
202             }
203              
204             sub _make_hash_get {
205 7     7   15 my ($self, $glob, $slot)=@_;
206 7     7   32 no strict 'refs';
  7         12  
  7         717  
207             *$glob=sub{
208 4     4   9 my $self=shift;
209 4 100 66     23 if(exists $self->{$slot} && defined $self->{$slot}){
210 3         5 return %{$self->{$slot}};
  3         16  
211             }else{
212 1         3 $self->{$slot}={};
213 1         3 return ();
214             }
215 7         43 };
216             }
217              
218             sub _make_hash_remove {
219 7     7   14 my ($self, $glob, $slot)=@_;
220 7     7   570 no strict 'refs';
  7         12  
  7         3953  
221             *$glob=sub{
222 0     0   0 my $self=shift;
223 0 0       0 if(@_){
224 0         0 delete $self->{$slot}->{shift} while @_;
225             }else{
226 0         0 $self->{$slot}={};
227             }
228 7         59 };
229             }
230              
231             sub make_initialize {
232 0     0 0 0 my $self=shift;
233 0         0 my %args=@_;
234 0 0       0 my $scalar_accessors=$args{'$'} if exists $args{'$'};
235 0 0       0 my $array_accessors=$args{'@'} if exists $args{'@'};
236 0         0 AutoCode::Initialzer->make_initialize_by_hash(\@_);
237             }
238              
239             ### From Initializer
240              
241             sub _compose_source {
242 24     24   33 my ($self, $hash, $pkg)=@_;
243             # $self->throw('pkg is required') unless defined $pkg;
244 24         33 my @scalar_attrs;
245 24 50       140 if(exists $hash->{'$'}){
246 24         33 push @scalar_attrs, @{$hash->{'$'}};
  24         60  
247             }
248 24         30 my @array_attrs;
249 24 50       57 if(exists $hash->{'@'}){
250 24         31 push @array_attrs, @{$hash->{'@'}};
  24         45  
251             }
252            
253 24         76 my $source = 'sub { my($dummy, @args)=@_;'."\n";
254             # The line below is for debug. It will run only when the made module is working
255             # $source .= "print 'I am in _init of '. ref(\$dummy) . '_____';";
256            
257             # $source .= "\$dummy->SUPER::_initialize(\@args);\n";
258 24 50 33     88 if(@scalar_attrs || @array_attrs){
259 24         42 $source .= 'my ('. join ',', map{"\$$_"} @scalar_attrs;
  46         133  
260 24 50       68 $source .= ', ' unless @scalar_attrs == 0;
261 25         75 $source .= join ',', map{
262 24         43 '$'. ($self->_deref_plural($_))[1]}@array_attrs;
263             # "\$$_"} @array_attrs_plural;
264 24         39 $source .= ')='."\n".'$dummy->_rearrange([qw(';
265 24         44 $source .= join ' ', @scalar_attrs;
266 25         60 $source .= ' '. join ' ', map{
267 24         45 ($self->_deref_plural($_))[1]}@array_attrs;
268             # @array_attrs_plural;
269 24         39 $source .= ')], @args);'."\n";
270 24         38 map {$source .=
  46         151  
271             "defined \$$_ and \$dummy->$_(\$$_);\n"} @scalar_attrs;
272            
273             # if the array ref is defined, assign the dereferenced into array,
274             # otherwise initialize the array by invoking remove_$plural
275 25         60 map {
276 24         51 my ($singular, $plural)= $self->_deref_plural($_);
277             # ($_, $schema->get_plural($_));
278 25         110 $source .= <
279             if(ref(\$$plural) eq'ARRAY'){
280             \$dummy->add_$singular(\$_) foreach (\@{\$$plural});
281             }else{
282             \$dummy->remove_$plural;
283             }
284             END_ACCESSORS
285             }@array_attrs;
286             }
287             # The following 3 lines are to replace 'the not-working SUPER with eval'
288             # It spends almost a whole afternoon of the second day of 2004.
289 24         40 $source .= "no strict 'refs';\n";
290 24         61 $source .= 'my $super=AutoCode::Root::_find_super("'. $pkg .'", "_initialize");'."\n";
291 24         33 $source .= '&{$super. "::_initialize"}($dummy, @args);'."\n";
292            
293             # $source .= "\$dummy->SUPER::_initialize(\@args);\n";
294             # $source .= "print '______' \. *{\$dummy->SUPER::_initialize} \. \"\\n\"";
295 24         29 $source .= '};'."\n";
296 24         112 $self->debug("$source");
297 24         80 return $source;
298             }
299              
300             sub _make_initialize_by_hash {
301 24     24   50 my ($self, $hash, $pkg, $method)=@_;
302 24         94 my $source = $self->_compose_source($hash, $pkg);
303 24   50     127 $method ||= '_initialize';
304 7     7   43 no strict 'refs';
  7         12  
  7         1756  
305 6 100   6   32 *{"$pkg\::$method"} = eval $source;
  6 100   5   11  
  6 100   5   459  
  5 100   4   41  
  5 100   2   15  
  5 100   2   398  
  5 100   6   27  
  5 100       9  
  5 100       392  
  4 100       38  
  4 100       6  
  4         268  
  2         11  
  2         4  
  2         178  
  2         17  
  2         4  
  2         127  
  24         4694  
  24         183  
  6         52  
  6         59  
  6         71  
  6         44  
  6         22  
  6         45  
  4         16  
  4         14  
  5         37  
  5         25  
  4         16  
  3         16  
  3         8  
  4         32  
  4         17  
  5         23  
  5         49  
  4         14  
  4         16  
  5         29  
  5         17  
  5         30  
  5         50  
  4         19  
  5         30  
  5         24  
  5         38  
  2         4  
  3         16  
  3         15  
  5         24  
  3         8  
  3         15  
  3         17  
  5         25  
  5         14  
  5         62  
  5         13  
  5         35  
  5         29  
  5         18  
  5         11  
  5         55  
306 24 50       174 $self->throw( "Error when eval'ing _initialize\n$@") if($@);
307             }
308            
309             sub _make_digest_args_by_hash {
310 0     0   0 my ($self, $hash, $pkg)=@_;
311 0         0 $self->_make_initialize_by_hash($hash, $pkg, '_digest_args');
312             }
313              
314             sub make_initialize_by_model {
315 18     18 0 29 my ($class, $model, $pkg)=@_;
316 18         43 my $schema=$model->schema;
317 18         42 my @scalar_attrs = $model->get_scalar_attributes;
318 18         41 my @array_attrs = $model->get_array_attributes;
319 18         32 my @array_attrs_plural= map {$schema->get_plural($_)} @array_attrs;
  14         38  
320 18         57 my %args=('$'=>\@scalar_attrs);
321 18         23 my @array_ones;
322 18         57 for(my $i=0; $i<@array_attrs; $i++){
323 14         60 push @array_ones, [$array_attrs[$i], $array_attrs_plural[$i]];
324             }
325 18         60 my %args=('$'=>\@scalar_attrs, '@'=> \@array_ones);
326 18         59 $class->_make_initialize_by_hash(\%args, $pkg);
327             }
328              
329             1;
330             __END__