File Coverage

blib/lib/Loompa.pm
Criterion Covered Total %
statement 91 101 90.1
branch 50 56 89.2
condition 42 50 84.0
subroutine 14 15 93.3
pod 5 5 100.0
total 202 227 88.9


line stmt bran cond sub pod time code
1             package Loompa;
2 3     3   120877 use strict;
  3         9  
  3         115  
3 3     3   18 use warnings;
  3         6  
  3         78  
4              
5 3     3   18 use Carp;
  3         10  
  3         2754  
6              
7             =head1 NAME
8              
9             Loompa - Lightweight object-oriented miniature Perl assistant.
10              
11             =head1 VERSION
12              
13             Version 0.51
14              
15             =cut
16              
17             our $VERSION = '0.51';
18              
19             =head1 WARNING
20              
21             This code is only here because some legacy code depends on it. Do not use it
22             in new code. Use L if you want an object/class builder.
23              
24             =head1 SYNOPSIS
25              
26             package MyCat;
27             use base qw/ Loompa /;
28              
29             sub methods {
30             [ qw/ name color temperment /]
31             }
32              
33             sub init {
34             my $self = shift;
35             $self->color( 'black' ) if $self->name eq 'Boris';
36             return $self;
37             }
38              
39             # in a nearby piece of code ...
40             use MyCat;
41              
42             my $cat = MyCat->new({
43             name => 'Boris',
44             temperment => 'evil',
45             });
46              
47             print $cat->name; # "Boris"
48             print $cat->temperment; # "evil"
49             print $cat->color; # "black"
50              
51             =head1 METHODS
52              
53             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54              
55             =head2 new([ \%properties ])
56              
57             Class method. C<\%properties> is optional. If provided, hash keys will be
58             taken as property names and hash values as property values.
59              
60             It is an error to supply a property for which you have not also created an
61             accessor method. In other words, if you do something like this:
62              
63             package Cat;
64             use base 'Loompa';
65             my $cat = Cat->new({ whiskers => 'long' });
66              
67             In this case, Loompa will C with "Method 'whiskers' not defined for
68             object."
69              
70             =cut
71              
72             # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
73             # contruct a new object and take all incoming arguments as
74             # method/value pairs
75             sub new {
76 13     13 1 4524 my $class = shift;
77 13         24 my( $properties ) = @_;
78              
79 13         22 $__PACKAGE__::LOOMPA_IS_BUILDING_ME = 1;
80 13 100 100     74 croak 'Argument to constructor must be hash reference'
81             if $properties and ref $properties ne 'HASH';
82              
83 12         28 my $self = bless {}, $class;
84 12 100       101 $self->make_methods( $self->methods )
85             if $self->can( 'methods' );
86 12         63 while( my( $property, $value ) = each %$properties ) {
87 9 100       38 croak qq/Method "$property" not defined for object; caller: /
88             . join ':' => caller
89             unless $self->can( $property );
90 8         21 $self->$property( $value );
91             }
92 11         43 $self->set_method_defaults;
93 11         52 $__PACKAGE__::LOOMPA_IS_BUILDING_ME = 0;
94              
95 11         39 $self->init( $properties );
96 11         73 $self;
97             }
98              
99             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
100              
101             =head2 init()
102              
103             Blank initializer; may be overridden.
104              
105             =cut
106              
107             # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
108             sub init {
109 8     8 1 10 my $self = shift;
110 8         13 $self;
111             }
112              
113             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114              
115             =head2 check_methods( \@methods ) OR check_methods( \%methods )
116              
117             Class and object method; enforces API. One of C<\@methods> or C<\%methods> is
118             required. If supplied, C<\@methods> must be a list of words. If supplied,
119             C<\%methods> must be a hash reference, with keys as words and values as one of
120             the following:
121              
122             =over 4
123              
124             =item * , in which case the default getter/setter method will be used.
125              
126             =item * , in which case the default getter/setter method will be used, and used as its default value.
127              
128             =item * , in which case will be used instead of the default getter/setter.
129              
130             =back
131              
132             =cut
133              
134             # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
135             # XXX pass { undef_ok => 1 } as $_options if you want $methods to be optional
136             sub check_methods {
137 41     41 1 736 my $proto = shift;
138 41         53 my( $methods, $_options ) = @_;
139              
140 41 100 66     191 if( $_options and $_options->{ undef_ok }) {
141 23 100       74 return unless $methods;
142             }
143             else {
144 18 100       61 croak '$methods is required' unless $methods;
145             }
146              
147 39         58 my $error = 'API error: please read the documentation for check_methods()';
148 39 100       110 if( ref $methods eq 'ARRAY' ) {
    100          
149 23         47 for( @$methods ) {
150 45 100 66     525 croak $error .' (invalid method name)'
      100        
      100        
151             if not defined $_
152             or $_ eq ''
153             or $_ =~ /\W/
154             or $_ =~ /^\d/; # FIXME duplicated
155             }
156 15         46 return scalar @$methods;
157             }
158             elsif( ref $methods eq 'HASH' ) {
159 13 100       49 croak $error .' (invalid hash reference)'
160             unless %$methods;
161 12         55 while( my( $key, $value ) = each %$methods ) {
162 23 100 33     275 croak $error .' (invalid method name)'
      66        
      100        
163             if not defined $key
164             or $key eq ''
165             or $key =~ /\W/
166             or $key =~ /^\d/; # FIXME duplicated
167 19 100 100     181 croak $error .' (invalid hash reference)'
      100        
168             if defined $value and ref $value and ref $value ne 'CODE';
169             }
170 4         18 return scalar keys %$methods;
171             }
172             else {
173 3         48 croak $error .' (invalid data type: argument to make_methods() must be arrayref or hashref)'
174             }
175 0         0 die 'Should never get here';
176             }
177              
178             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
179              
180             =head2 make_methods( \@methods, [ $subref ], \%options ) OR make_methods( \%methods, undef, \%options )
181              
182             Class and object method. Makes methods for items in C<$methods>.
183              
184             If <$methods> is an array reference, one method will be created for each name
185             in the list. If supplied, C<$subref> must be a subroutine reference, and will
186             be used in place of the standard setter/getter.
187              
188             If <%options> is provided, these are legal values:
189              
190             =over 4
191              
192             =item override_existing
193              
194             Loompa's default behavior is to create methods only once, thereafter returning
195             before the construction step. If you set C to true, each
196             method provided will be constructed anew.
197              
198             =item object
199              
200             Loompa's default behavior is to pass the object in method-call style.
201              
202             This is probably undesired behavior in a base class that defines many custom
203             class methods. Setting the 'object' value to a package name will override the
204             default passed-in object and provide that instead. Note that this is done
205             through a closure and may not be reasonable on your memory usage if you want to
206             define lots of methods.
207              
208             You can also do dirtier things with this option, but I'm going to refrain from
209             describing them.
210              
211             =back
212              
213             if C<$methods> is a hash reference, the key/value pair will be understood as
214             C<$method_name> and C<$method_subroutine>. For example:
215              
216             CatClass->make_methods({
217             boris => undef,
218             sasha => $method_cat,
219             shaolin => $method_fat_cat,
220             });
221             my $cat = CatClass->new;
222             $cat->boris; # calls default setter/getter
223             $cat->sasha; # calls the method referenced by C<$method_cat>
224             $cat->shaolin; # calls the method referenced by C<$method_fat_cat>
225              
226             In this case,
227              
228             PLEASE NOTE that the second argument to your custom subroutine will be the name
229             of the subroutine as it was called. In other words, you should write something
230             like this:
231              
232             package MyClass;
233             use base qw/ Loompa /;
234              
235             my $color_method = sub {
236             my $self = shift;
237             my( $name, $emotion ) = @_;
238             return "My name is '$name' and I am '$emotion.'";
239             };
240             MyClass->make_methods( 'orange', 'brown', $color_method );
241              
242             MyClass->orange( 'happy' ); ## "My name is 'orange' and I am 'happy.'"
243             MyClass->brown( 'sad' ); ## "My name is 'brown' and I am 'sad.'"
244              
245             =cut
246              
247             # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
248             sub make_methods {
249 24     24 1 4449 my $proto = shift;
250 24         39 my( $methods, $subref, $options ) = @_;
251              
252 24 100       123 return unless $methods; # XXX does this make undef_ok obsolete?
253 22         110 $proto->check_methods( $methods, { undef_ok => 1 });
254 15 100       59 if( ref $methods eq 'ARRAY' ) {
    50          
255             $proto->_make_method( $_, $subref, $options )
256 13         105 for @$methods;
257             }
258             elsif( ref $methods eq 'HASH' ) {
259 2         10 while( my( $property, $prototype ) = each %$methods ) {
260 9         24 $proto->_make_method( $property, $prototype, $options );
261             }
262             }
263 15         57 return $proto;
264             }
265              
266             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
267             # create an accessor method $field in the calling package
268             # Creates a getter/setter method for C<$name>. If supplied, C<$subref> must be a
269             # subroutine reference, and will be used in place of the standard setter/getter.
270             sub _make_method {
271 44     44   1343 my $proto = shift;
272 44         75 my( $name, $prototype, $options ) = @_;
273              
274 44   100     157 $options ||= {}; # makes it easier to evaluate later
275 44 100 100     218 croak 'Second argument, if supplied, must be scalar value or subroutine reference'
      66        
276             if defined $prototype and ( ref $prototype and not ref $prototype eq 'CODE' );
277              
278 42         48 my( $default, $subref );
279 42 100       100 if( ref $prototype eq 'CODE' ) {
280 17         25 $subref = $prototype;
281             }
282              
283 42   66     119 my $package = ref $proto || $proto;
284 42 100 100     47 return if defined &{ $package ."::$name" } and not $options->{ override_existing };
  42         279  
285              
286 3     3   21 no warnings qw/ redefine /;
  3         5  
  3         109  
287 3     3   15 no strict qw/ refs /;
  3         5  
  3         1298  
288 33 100       64 if( $subref ) {
289 17 100       47 if ($options->{object}) {
290 6         9 my $object = $options->{object};
291 6     10   21 *{ $package ."::$name" } = sub { shift; $subref->( $object, $name, @_ )};
  6         46  
  10         4637  
  10         29  
292             } else {
293 11     16   56 *{ $package ."::$name" } = sub { $subref->( shift, $name, @_ )};
  11         124  
  16         1968  
294             }
295             }
296             else {
297 16 50       36 if ($options->{object}) {
298 0         0 my $object = $options->{object};
299 0         0 *{ $package ."::$name" } = sub {
300 0     0   0 shift;
301 0         0 my $self = $object;
302              
303 0 0       0 return $self->{ $name } unless @_;
304 0         0 $self->{ $name } = shift;
305 0 0       0 croak 'Please pass only one value'
306             if @_;
307 0         0 $self->{ $name };
308 0         0 };
309             } else {
310 16         109 *{ $package ."::$name" } = sub {
311 60     60   683 my $self = shift;
312              
313 60 100       270 return $self->{ $name } unless @_;
314 26         47 $self->{ $name } = shift;
315 26 100       82 croak 'Please pass only one value'
316             if @_;
317 25         99 $self->{ $name };
318 16         60 };
319             }
320             }
321             }
322              
323             # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
324              
325             =head2 set_method_defaults()
326              
327             Object method; sets default values for accessors, as defined by local
328             C method.
329              
330             =cut
331              
332             # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
333             sub set_method_defaults {
334 13     13 1 528 my $self = shift;
335              
336             return
337 13 100 66     72 unless $self->can( 'methods' ) and ref $self->methods and ref $self->methods eq 'HASH';
      100        
338              
339 2         32 my %properties = %{ $self->methods };
  2         5  
340 2         30 while( my( $property, $value ) = each %properties ) {
341 10 100       45 next if defined $self->$property;
342 3 100       27 $self->$property( $value )
343             if defined $value;
344             }
345 2         17 $self;
346             }
347              
348              
349             'loompa';
350              
351             __END__