File Coverage

blib/lib/Class/Constructor.pm
Criterion Covered Total %
statement 114 120 95.0
branch 48 58 82.7
condition 10 17 58.8
subroutine 12 12 100.0
pod 1 1 100.0
total 185 208 88.9


line stmt bran cond sub pod time code
1              
2              
3 7     7   218095 use strict;
  7         17  
  7         279  
4 7     7   200 use 5.005;
  7         23  
  7         349  
5             package Class::Constructor;
6 7     7   44 use Carp;
  7         19  
  7         678  
7 7     7   43 use File::Spec;
  7         29  
  7         200  
8              
9 7     7   34 use vars qw($VERSION);
  7         14  
  7         1180  
10              
11             $VERSION = '1.1.4';
12              
13             =head1 NAME
14              
15             Class::Constructor - Simplify the creation of object constructors
16              
17             =head1 SYNOPSIS
18              
19             package MyPackage;
20              
21             # Note if you don't have the CLASS package installed,
22             # you can use the __PACKAGE__ keyword instead
23              
24             use CLASS;
25             use base qw/ Class::Constructor Class::Accessor /;
26              
27             my @Accessors = qw(
28             some_attribute
29             another_attribute
30             yet_another_attribute
31             );
32              
33             CLASS->mk_accessors(@Accessors);
34             CLASS->mk_constructor(
35             Name => 'new',
36             Auto_Init => \@Accessors,
37             );
38              
39             =head1 DESCRIPTION
40              
41             Simplifies the creation of object constructors.
42              
43             Instead of writing:
44              
45             sub new {
46             my $proto = shift;
47             my $class = ref $proto || $proto;
48             my $self = {};
49             bless $self, $class;
50              
51             my %args = @_;
52             foreach my $attr ('first_attribute', 'second_attribute') {
53             $self->$attr($args{$attr});
54             }
55              
56             $self->_init();
57              
58             return $self;
59             }
60              
61             You can just write:
62              
63             CLASS->mk_constructor(
64             Auto_Init => [ 'first_attribute', 'second_attribute' ],
65             );
66              
67             There are other features as well:
68              
69             =over 4
70              
71             =item Automatically call other initialization methods.
72              
73             Using the C method of C,
74             you can have your constructor method automatically call
75             one or more initialization methods.
76              
77             =item Automatic Construction of objects of Subclasses
78              
79             Your constructor can bless objects into one of
80             its subclasses.
81              
82             For instance, the C class could bless objects
83             into the C or C classes
84             depending on a parameter passed to the constructor.
85              
86             See L for details.
87              
88             =back
89              
90             =head1 METHOD
91              
92             =head2 mk_constructor
93              
94             CLASS->mk_constructor(
95             Name => 'new',
96             Init_Methods => [ '_init' ],
97             Subclass_Param => 'Package_Type',
98             Auto_Init => [ 'first_attribute', 'second_attribute' ],
99             );
100              
101             The C method creates a constructor named C in
102             C's namespace.
103              
104             =over 4
105              
106             =item Name
107              
108             The name of the constructor method. The default is C.
109              
110             =item Init_Methods
111              
112             Cause the created constructor to call the listed methods
113             on all new objects that are created via the constructor.
114              
115             Foo->mk_constructor(
116             Name => 'new',
117             Init_Methods => '_init',
118             );
119              
120             my $object = Foo->new; # This calls $object->_init();
121              
122              
123             Foo->mk_constructor(
124             Name => 'new',
125             Init_Methods => [ '_init', '_startup' ],
126             );
127              
128             my $object = Foo->new; # after construction, new()
129             # calls $object->_init(),
130             # then $object->_startup()
131              
132              
133             =item Auto_Init
134              
135             A list of attributes that should be automatically initialized via the
136             parameters to the constructor.
137              
138             For each name/value pair passed to the constructor, the constructor
139             will call the method named C with the parameter of C.
140              
141             For instance, if you make your constructor with:
142              
143             Fruit->mk_constructor(
144             Auto_Init => [ 'size', 'colour' ],
145             );
146              
147             And you call the constructor with:
148              
149             use Fruit;
150             my $fruit = Fruit->new(
151             size => 'big',
152             colour => 'red',
153             );
154              
155             Then, internally, the C constructor will automatically call the
156             following methods:
157              
158             $fruit->size('big');
159             $fruit->colour('red');
160              
161             Note that by default, C converts names to lower
162             case. See C, below.
163              
164             =item Required_Params
165              
166             A list of params that must be passed to the constructor when the object
167             is created. If these items are not already listed as C
168             methods, they will be added to the C list.
169              
170             Fruit->mk_constructor(
171             Required_Params => [ 'size', 'price' ],
172             );
173              
174             package main;
175              
176             use Fruit;
177             my $fruit = Fruit->new; # error, missing size, price
178              
179             my $fruit = Fruit->new( # error: missing price
180             size => 'big'
181             );
182              
183             my $fruit = Fruit->new( # okay
184             size => 'big',
185             price => 0.25,
186             );
187              
188              
189             =item Disable_Case_Mangling
190              
191             Set this to a true value if you don't want Class::Constructor to force
192             attribute names to lower case. See C, below.
193              
194             =item Disable_Name_Normalizing
195              
196             Another name for C, above.
197              
198             =item Method_Name_Normalizer
199              
200             Custom subroutine for converting a parameter passed to auto_init into a
201             attribute name. See C, below.
202              
203             =item Class_Name_Normalizer
204              
205             Custom subroutine for converting a subtype class into a Perl class name.
206             See C, below.
207              
208             =item Param_Name_Normalizer
209              
210             Custom subroutine to be applied to params passed to the constructor in
211             order to recognize special ones, such as those that are required by
212             C and the special C. See C
213             SENSITIVITY>, below.
214              
215             =item Subclass_Param
216              
217             You can cause the constructor to make instances of a subclass,
218             based on the a special parameter passed to the constructor:
219              
220             # Fruit.pm:
221             package Fruit;
222             Fruit->mk_constructor(
223             Name => 'new',
224             Subclass_Param => 'Type',
225             );
226              
227             sub has_core { 0 };
228              
229             # Fruit/Apple.pm:
230             package Fruit::Apple;
231             use base 'Fruit';
232              
233             sub has_core { 1 };
234              
235             # main program:
236             package main;
237              
238             my $apple = Fruit->new(
239             Type => 'Apple',
240             );
241              
242             if ($apple->has_core) {
243             print "apples have cores!\n";
244             }
245              
246             =item Dont_Load_Subclasses_Param
247              
248             The name of the parameter that will be checked by the constructor
249             to determine whether or not subclasses specified by C
250             will be loaded or not. This is mainly useful if you are writing
251             test scripts and you want to load in your packages manually.
252              
253             For instance:
254              
255             # Fruit.pm:
256             package Fruit;
257             Fruit->mk_constructor(
258             Name => 'new',
259             Subclass_Param => 'type',
260             Dont_Load_Subclass_Param => 'Dont_Load_Subclass',
261             );
262              
263             # main program:
264             package main;
265              
266             my $apple = Fruit->new(
267             Type => 'Apple',
268             Dont_Load_Subclass => 1,
269             );
270              
271             Now when the C<$apple> object is created, the constructor makes no
272             attempt to require the C module.
273              
274             =back
275              
276             =head1 CASE SENSITIVITY
277              
278             By default, attribute names are forced to lower case and
279             the case of C parameter names passed to the constructor
280             doesn't matter.
281              
282             So the following call to a constructor:
283              
284             my $fruit = Fruit->new(
285             SiZE => 'big',
286             colOUR => 'red',
287             );
288              
289             Is actually equivalent to:
290              
291             my $fruit = Fruit->new();
292             $fruit->size('big');
293             $fruit->colour('red');
294              
295             You can disable this behaviour by setting C
296             to a true value:
297              
298             package Fruit;
299             Fruit->mk_constructor(
300             Disable_Case_Mangling => 1,
301             );
302              
303             Now the parameters are left unchanged:
304              
305             my $fruit = Fruit->new(
306             SiZE => 'big',
307             colOUR => 'red',
308             );
309              
310             # equivalent to:
311             my $fruit = Fruit->new();
312             $fruit->SiZE('big');
313             $fruit->colOUR('red');
314              
315              
316             Similarly for class names as passed via C, they are
317             converted to lower case and then the first letter is uppercased.
318              
319             # the following creates a Fruit::Apple
320             my $apple = Fruit->new(
321             Type => 'APPLE',
322             );
323              
324             This behaviour is also disabled via C:
325              
326             package Fruit;
327             Fruit->mk_constructor(
328             Subclass_Param => 'Type',
329             Disable_Case_Mangling => 1,
330             );
331              
332             # the following creates a Fruit::APPLE
333             my $apple = Fruit->new(
334             Type => 'APPLE',
335             );
336              
337             =head2 Advanced: Customizing Class, Method and Param normalization.
338              
339             Note that this is an advanced feature with limited use, so you can
340             probably skip it.
341              
342             If you want to customize the way C changes method
343             names, you can pass subroutines to do the work:
344              
345             package Fruit;
346             Fruit->mk_constructor(
347             Subclass_Param => 'Type',
348             Method_Name_Normalizer => sub { '_' . lc $_[0] }, # precede lc methods with underscore
349             Param_Name_Normalizer => sub { uc $_[0] }, # params compared as upper case
350             Class_Name_Normalizer => sub { uc $_[0] }, # class names to uppercase
351             Required_Params => [ 'Size' ],
352             );
353              
354             # the following creates a Fruit::APPLE
355             my $apple = Fruit->new(
356             Type => 'apple',
357             SiZE => 'big',
358             colOUR => 'red',
359             );
360              
361             # and the above is equivalent to:
362             my $apple = Fruit->new(
363             type => 'apple',
364             );
365              
366             $apple->_SiZE('big');
367             $apple->_colOUR('red');
368              
369             In the example above, the C causes auto_init to
370             make convert parameter names into method names as follows:
371              
372             SiZE => _size
373             colOUR => _colour
374              
375             The C converts the value of C (the
376             C) into method names as follows:
377              
378             apple => APPLE
379              
380             The C converts param names to upper case before
381             comparing them. So C is specified to be C, and is
382             eventually passed as C. But since both are normalized to C,
383             the match is found.
384              
385             =cut
386              
387             sub mk_constructor {
388 15     15 1 16993 my $proto = shift;
389 15   33     105 my $class = ref $proto || $proto;
390              
391 15         72 my %params = @_;
392              
393 15   100     80 my $constructor_name = $params{Name} || 'new';
394              
395             {
396 7     7   35 no strict 'refs';
  7         18  
  7         6901  
  15         26  
397 15 50       29 return if defined &{"$class\:\:$constructor_name"};
  15         161  
398             }
399              
400 15         28 my $normalization = 1;
401 15 100       55 undef $normalization if $params{Disable_Name_Normalization};
402 15 100       50 undef $normalization if $params{Disable_Case_Mangling};
403              
404 15   100 53   173 my $method_name_normalize = $params{Method_Name_Normalizer} || sub { lc $_[0] };
  53         165  
405 15   100 56   99 my $param_name_normalize = $params{Param_Name_Normalizer} || sub { lc $_[0] };
  56         212  
406 15   100 2   95 my $class_name_normalize = $params{Class_Name_Normalizer} || sub { ucfirst lc $_[0] };
  2         9  
407              
408 15 100       87 my $subclass_param_name = $normalization ? &$param_name_normalize($params{Subclass_Param})
409             : $params{Subclass_Param};
410              
411              
412 15         76 my $dont_load_subclass_param = $params{Dont_Load_Subclass_Param};
413              
414 15         50 foreach my $param (qw/Auto_Init Init_Method Init_Methods/) {
415 45 100       169 next unless exists $params{$param};
416 17 100       94 $params{$param} = [ $params{$param} ] unless ref $params{$param} eq 'ARRAY';
417             }
418              
419 15         50 my @init_methods;
420 15 100       59 push @init_methods, @{ $params{'Init_Method'} } if exists $params{'Init_Method'};
  2         4  
421 15 100       46 push @init_methods, @{ $params{'Init_Methods'} } if exists $params{'Init_Methods'};
  1         2  
422              
423 15         24 my @auto_init;
424 15 100       69 push @auto_init, @{ $params{'Auto_Init'} } if exists $params{'Auto_Init'};
  14         59  
425              
426              
427 15         21 my @required_params;
428 15 100       46 if (exists $params{'Required_Params'}) {
429 3 50       11 if ($normalization) {
430 3         6 push @required_params, map { &$param_name_normalize($_) } @{ $params{'Required_Params'} };
  6         25  
  3         10  
431             }
432             else {
433 0         0 push @required_params, @{ $params{'Required_Params'} };
  0         0  
434             }
435             }
436              
437 15         29 my %auto_init;
438              
439 15         39 foreach my $param (@required_params) {
440 6 50       19 unless ($auto_init{$param}) {
441 6         15 push @auto_init, $param;
442 6         62 $auto_init{$param} = 1;
443             }
444             }
445              
446              
447 15 100       68 if ($normalization) {
448 13         28 %auto_init = map { &$method_name_normalize($_) => 1 } @auto_init;
  36         108  
449             }
450             else {
451 2         9 %auto_init = map { $_ => 1 } @auto_init;
  6         16  
452             }
453              
454             my $constructor = sub {
455 26     26   28888 my $proto = shift;
456 26   33     164 my $class = ref $proto || $proto;
457              
458 26         105 my %params = @_;
459 26         45 my $self = {};
460              
461 26         40 my %normalized_params;
462              
463 26 100       64 if ($normalization) {
464 23         74 %normalized_params = map { &$param_name_normalize($_) => $params{$_}} keys %params;
  47         150  
465             }
466             else {
467 3         8 %normalized_params = map { $_ => $params{$_} } keys %params;
  8         20  
468             }
469              
470 26         99 my $load_subclasses = 1;
471              
472 26 50       92 if (defined $dont_load_subclass_param) {
473 0 0 0     0 if (exists $params{$dont_load_subclass_param} and $params{$dont_load_subclass_param}) {
474 0         0 delete $params{$dont_load_subclass_param};
475 0         0 $load_subclasses = 0;
476             }
477             }
478              
479              
480             # Check for parameters flagged as required. Throw an exception if
481             # there is one missing.
482              
483 26         36 my @missing_required;
484 26         56 foreach my $required_param (@required_params) {
485 18 50       56 if ($normalization) {
486 18 100       34 next if exists $normalized_params{ &$param_name_normalize($required_param) };
487             }
488             else {
489 0 0       0 next if exists $params{ $required_param };
490             }
491 6         25 push @missing_required, $required_param;
492             }
493 26 100       1425 if (@missing_required) {
494 4         583 die "$class: Missing required parameter(s): ". (join ', ', @missing_required). "\n";
495             }
496              
497 22 100       68 if ($subclass_param_name) {
498              
499 9         13 my $subclass;
500              
501 9 100       27 if ($normalization) {
502              
503 7 100       669 if (exists $normalized_params{$subclass_param_name}) {
504 3         17 $subclass = &$class_name_normalize($normalized_params{$subclass_param_name});
505             }
506             }
507             else {
508             # subclass param is fixed
509 2 100       622 if (exists $params{$subclass_param_name}) {
510 1         2 $subclass = $params{$subclass_param_name};
511             }
512             }
513              
514 9 100       34 if ($subclass) {
515 4         17 $class .= "::$subclass";
516              
517 4 50       19 if ($load_subclasses) {
518 4         27 my @class_fn = split /::/, $class;
519 4         139 my $class_fn = File::Spec->join(split /::/, $class);
520 4         12 $class_fn .= '.pm';
521              
522 4         13278 require $class_fn;
523             }
524             }
525             }
526              
527 22         2825 bless $self, $class;
528              
529 22         84 foreach my $attr (keys %params) {
530 48 100       449 my $method = $normalization ? &$method_name_normalize($attr) : $attr;
531 48 100       161 if ($auto_init{$method}) {
532 46         213 $self->$method($params{$attr});
533             }
534             else {
535 2 100       8 unless (@init_methods) {
536 1         221 croak "Can't autoinitialize method $method from $attr\n";
537             }
538             }
539             }
540              
541 21         159 foreach my $init_method (@init_methods) {
542 4         29 $self->$init_method(@_);
543             }
544              
545 21         815 return $self;
546 15         178 };
547              
548             {
549 7     7   74 no strict 'refs';
  7         14  
  7         871  
  15         27  
550 15         43 *{"$class\:\:$constructor_name"} = $constructor;
  15         98  
551             }
552 15         124 return 1;
553             }
554              
555             1;
556              
557             =head1 AUTHOR
558              
559             Michael Graham Emag-perl@occamstoothbrush.comE
560              
561             Copyright (C) 2001 Michael Graham. All rights reserved.
562             This program is free software. You can use, modify,
563             and distribute it under the same terms as Perl itself.
564              
565             The latest version of this module can be found on http://www.occamstoothbrush.com/perl/
566              
567             =head1 SEE ALSO
568              
569             =over 4
570              
571             =item Class::Accessor
572              
573             =item CLASS
574              
575             =back
576              
577             =cut