File Coverage

blib/lib/Class/Maker/Basic/Constructor.pm
Criterion Covered Total %
statement 13 59 22.0
branch 0 22 0.0
condition 0 8 0.0
subroutine 5 7 71.4
pod n/a
total 18 96 18.7


line stmt bran cond sub pod time code
1             #
2             # Author: Murat Uenalan (muenalan@cpan.org)
3             #
4             # Copyright: Copyright (c) 1997 Murat Uenalan. All rights reserved.
5             #
6             # Note: This program is free software; you can redistribute it and/or modify it
7             #
8             # under the same terms as Perl itself.
9              
10             package Class::Maker::Basic::Constructor;
11              
12 8     8   41 require 5.005_62; use strict; use warnings;
  8     8   12  
  8         240  
  8         39  
  8         12  
  8         218  
13              
14 8     8   42 use Exporter;
  8         14  
  8         5420  
15              
16             our $VERSION = "0.06";
17              
18             our @ISA = qw(Exporter);
19              
20             our %EXPORT_TAGS = ( 'all' => [ qw(new) ], 'std' => [ qw(new) ] );
21              
22             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
23              
24             our @EXPORT = qw();
25              
26             # Preloaded methods go here.
27              
28             our $DEBUG = 0;
29              
30             our @init_methods = qw( init initialize );
31              
32             sub simple_new
33             {
34 0     0     my $this = shift;
35              
36 0   0       bless {}, ref( $this ) || $this;
37             }
38              
39             =pod
40              
41             "overriden attribute-names" are not dramatic, because every attribute
42             gets its classname prepended like "Object::attribute" into the hash
43             representation of the object.
44              
45             But you must be aware that when initializing via new( public => ),
46             alwas the first parent attribute is used for the initalization.
47              
48             new( Parent1::field => 'bla', Parent2::field => 'blabla' );
49              
50             =cut
51              
52             # multiple inheritance constructor (shouldn't be overriden, otherwise no MI)
53              
54             sub new
55             {
56 0     0     my $what = shift;
57              
58 0   0       my $class = ref( $what ) || $what;
59              
60             # convert constructor arguments to accessor/method calls
61              
62 0           my @args = @_;
63            
64 0           my %args;
65            
66 0 0         if( $class->can( '_arginit' ) )
67             {
68 0           $class->_arginit( \@args );
69             }
70              
71 0           %args = @args;
72            
73 0           my $args = \%args;
74              
75 0 0         warn( "_filter_argnames" ) if $Class::Maker::DEBUG;
76              
77 0           _filter_argnames( $args );
78              
79             # look if we just need cloning
80              
81 0 0         if( ref( $what ) )
82             {
83 0           my %copy = %$what;
84              
85 0           my $clone = bless \%copy, $class;
86              
87 0           while( my ( $key, $value ) = each %args )
88             {
89 0           $clone->$key( $value );
90             }
91              
92 0           return $clone;
93             }
94              
95             # if we do not clone, construct a new instance
96              
97 0           my $this = bless {}, $class;
98              
99             # preset all defaults
100              
101             # my $rfx = Class::Maker::Reflection::reflect( $class ) or die;
102             # if( $rfx->definition )
103             # {
104             # warn( "init defaults" ) if $Class::Maker::DEBUG;
105             # _init_by_args( $this, $rfx->definition->{default} ) if exists $rfx->definition->{default};
106             # }
107              
108             # inheriting attributes here
109              
110 0 0         warn( sprintf "NEW TRAVERSING ISA: %s", join( ', ', @{ Class::Maker::Reflection::inheritance_isa( ref( $this ) ) } ) ) if $Class::Maker::DEBUG;
  0            
111              
112 0   0       foreach my $parent ( @{ Class::Maker::Reflection::inheritance_isa( ref( $this ) || die ) } )
  0            
113             {
114 0           my $class = ref($this);
115              
116 0           bless $this, $parent;
117              
118 8     8   53 no strict 'refs';
  8         16  
  8         3890  
119            
120              
121              
122             # before _preinit, we init from class { default => { .. } }
123              
124 0 0         my $rfx = Class::Maker::Reflection::reflect( $parent ) or die;
125              
126 0 0         if( defined $rfx->definition )
127             {
128 0 0         warn "*WARN* class default => { .. } initing from class $parent with default = ".Data::Dump::pp($rfx->definition->{default}) if $Class::Maker::DEBUG;
129              
130 0 0         _init_by_args( $this, $rfx->definition->{default}, $parent ) if exists $rfx->definition->{default};
131             }
132             else
133             {
134 0           warn "*WARN* class default => { .. } initing failed because reflex for class $parent was not found";
135             }
136              
137              
138              
139              
140              
141 0 0         "${parent}::_preinit"->( $this, $args ) if defined *{ "${parent}::_preinit" }{CODE};
  0            
142              
143              
144              
145              
146              
147              
148 0           foreach my $init_method ( @init_methods )
149             {
150 0 0         if( defined *{ "${parent}::${init_method}" }{CODE} )
  0            
151             {
152 0           "${parent}::${init_method}"->( $this, $args );
153              
154 0           last;
155              
156              
157             }
158             }
159              
160              
161             # init from new( args.. )
162              
163 0           my @args_found = _init_by_args( $this, $args, $parent );
164              
165 0           delete $args->{$_} for @args_found;
166              
167              
168              
169              
170 0 0         "${parent}::_postinit"->( $this, $args ) if defined *{ "${parent}::_postinit" }{CODE};
  0            
171              
172              
173              
174 0           bless $this, $class;
175             }
176              
177             # call constructor arguments as functions, because we assume attribute-handlers
178              
179 0           warn "Unhandled new() arg: '$_' (Implement attribute-handler or check spelling)" for keys %args;
180              
181 0           return $this;
182             }
183              
184             # functions
185              
186             sub _init_by_args
187             {
188 8     8   10777 use Data::Dump qw(pp);
  0            
  0            
189              
190             warn "_init_by_args: with args = ", Data::Dump::pp( @_ ), "\n" if $Class::Maker::DEBUG;
191              
192             my $this = shift;
193              
194             my $args = shift;
195              
196             my $parent = shift || ref($this);
197              
198            
199              
200             my @result;
201              
202             foreach my $attr ( keys %{$args} )
203             {
204             my $derefer;
205              
206             $derefer = "${parent}::${attr}";
207              
208             $derefer = $attr if $attr =~ /::/;
209              
210             # if( $Class::Maker::explicit )
211             # {
212             # $derefer = $attr;
213             # }
214              
215             no strict 'refs';
216              
217             if( my $coderef = $this->can( $derefer ) ) #defined *{ $derefer }{CODE} )
218             {
219             warn "_init_by_args: Setting $this default $derefer ($coderef) = ", $args->{$attr}, "\n" if $Class::Maker::DEBUG;
220              
221             $coderef->( $this, $args->{$attr} );
222              
223             # $derefer->( $this, $args->{$attr} );
224            
225             push @result, $attr;
226             }
227             else
228             {
229             use Carp qw(cluck);
230             cluck "*WARNING (default init failed)* method/derefer $derefer is not defined anywhere (public/private section)." if $Class::Maker::DEBUG;
231             }
232              
233             }
234              
235             warn sprintf "_init_by_args finishes with this = %s", pp( $this ) if $Class::Maker::DEBUG;
236              
237             return @result;
238             }
239              
240             sub _filter_argnames
241             {
242             my $temp = shift;
243              
244             # rename all -arg or --arg fields
245              
246             foreach my $key ( keys %$temp )
247             {
248             if( $key =~ /^\-+(.*)/ )
249             {
250             $temp->{$1} = $temp->{$key};
251              
252             delete $temp->{$key};
253             }
254             }
255             }
256              
257             sub _defaults
258             {
259             my $this = shift;
260              
261             my $args = shift;
262              
263             no strict 'refs';
264              
265              
266             foreach my $attr ( keys %$args )
267             {
268             # if( my $coderef = $this->can( $attr ) )
269             # {
270             # print "Setting $this default (via coderef $coderef) $attr = ", $args->{$attr}, "\n" if $Class::Maker::DEBUG;
271              
272             # $coderef->( $this, $args->{$attr} );
273              
274             # #$this->$attr( $args->{$attr} );
275            
276             $this->{$attr} = $args->{$attr};
277             # }
278             }
279             }
280              
281             1;
282              
283             __END__
284              
285             # cookbook says in Recipe 13.10
286             # my $self = bless {}, $class;
287             #
288             # for my $class (@ISA) {
289             # my $meth = $class . "::_init";
290             # $self->$meth(@_) if $class->can("_init");
291             # }
292              
293             # This calls a parent method with our object/package.
294             # "This is very fragile code" as stated in the cookbook
295             # recipe 13.10, which breaks into unusability when we
296             # have following scenario:
297             #
298             # The "_init" method of the parent class contains method calls
299             # of his own class and this method is overriden in this class.
300             #
301             # What happens is that within the init method of the foreign
302             # class the overriden method of the child class is called which
303             # in most cases leads to wrong initialization of our object.
304             #
305             # Further: The main problem is that we call a parent method
306             # with an object blessed in our current package !
307             #
308             # SOLUTION: Correctly create a parent object (which leads to
309             # the right blessing and therefore for correct package/object
310             # scenario) and simply copy the attributes of the parent
311             # to the child.
312              
313             # store old package/class name
314              
315              
316             =head1 NAME
317              
318             Class::Maker - classes, reflection, schemas, serialization, attribute- and multiple inheritance
319              
320             =head1 SYNOPSIS
321              
322             use Class::Maker qw(:all);
323              
324             class Something;
325              
326             class Person,
327             {
328             isa => [ 'Something' ],
329              
330             public =>
331             {
332             scalar => [qw( name age internal )],
333             },
334              
335             private
336             {
337             int => [qw( internal )],
338             },
339             };
340              
341             sub Person::hello
342             {
343             my $this = shift;
344              
345             $this->_internal( 2123 ); # the private one
346              
347             printf "Here is %s and i am %d years old.\n", $this->name, $this->age;
348             };
349              
350             my $p = Person->new( name => Murat, age => 27 );
351              
352             $p->hello;
353              
354              
355             =head1 DESCRIPTION
356              
357             This package descibes the default constructor functionality. It is central to L<Class::Maker> because during its call reflection, initialization, inheritance gets handled.
358              
359             =head2 SPECIAL METHODS
360              
361             =head3 sub _arginit : method
362              
363             Once this method exists in the package of the class it is called right after L<new()> was dispatched. It is generally for the modification of the C<@_> arguments to a convinient way C<new()> can handle it (It always expects a hash, but with this function one could translate an array to the hash).
364              
365             =head3 sub _preinit : method
366              
367             =head3 sub _postinit : method
368              
369             <& /maslib/signatures.mas:author_as_pod, &>