File Coverage

lib/Class/Dot.pm
Criterion Covered Total %
statement 206 208 99.0
branch 45 50 90.0
condition 10 14 71.4
subroutine 39 39 100.0
pod 4 4 100.0
total 304 315 96.5


line stmt bran cond sub pod time code
1             # $Id: Dot.pm 47 2007-11-03 21:11:17Z asksol $
2             # $Source: /opt/CVS/Getopt-LL/lib/Class/Dot.pm,v $
3             # $Author: asksol $
4             # $HeadURL: https://class-dot.googlecode.com/svn/branches/stable-1.5.0/lib/Class/Dot.pm $
5             # $Revision: 47 $
6             # $Date: 2007-11-03 22:11:17 +0100 (Sat, 03 Nov 2007) $
7             package Class::Dot;
8              
9 4     4   39033 use strict;
  4         9  
  4         114  
10 4     4   17 use warnings;
  4         6  
  4         87  
11 4     4   3775 use version qw(qv);
  4         12113  
  4         36  
12 4     4   273 use 5.006000;
  4         17  
  4         242  
13              
14 4     4   22 use Carp qw(croak);
  4         7  
  4         255  
15 4     4   2651 use Class::Dot::Types qw(:std);
  4         10  
  4         23  
16              
17             our $VERSION = qv('1.5.0');
18             our $AUTHORITY = 'cpan:ASKSH';
19              
20             my @EXPORT_OK = qw(
21             property after_property_set after_property_get
22             );
23              
24             push @EXPORT_OK, @Class::Dot::Types::STD_TYPES;
25              
26             my $INTERNAL_ATTR_NOISE = '__x__';
27              
28             my %EXPORT_CLASS = (
29             ':std' => [@EXPORT_OK],
30             );
31              
32             our %OPTIONS_FOR = ();
33             our %PROPERTIES_FOR = ();
34              
35             my %__TYPE_DICT__ = (
36             'Array' => \&isa_Array,
37             'Code' => \&isa_Code,
38             'Data' => \&isa_Data,
39             'File' => \&isa_File,
40             'Hash' => \&isa_Hash,
41             'Int' => \&isa_Int,
42             'Object' => \&isa_Object,
43             'String' => \&isa_String,
44             );
45              
46             sub import { ## no critic
47 13     13   7775 my $this_class = shift;
48 13         28 my $caller_class = caller;
49              
50 13         33 my $options = { };
51 13         20 my $export_class;
52             my @subs;
53 13         29 for my $arg (@_) {
54 29 100       394 if ($arg =~ m/^-/xms) {
    100          
55 13         36 $options->{$arg} = 1;
56             }
57             elsif ($arg =~ m/^:/xms) {
58 10 100       350 croak( 'Only one export class can be used. '
59             ."(Used already: [$export_class] now: [$arg])")
60             if $export_class;
61              
62 9         22 $export_class = $arg;
63             }
64             else {
65 6         11 push @subs, $arg;
66             }
67             }
68 12         27 $OPTIONS_FOR{$caller_class} = $options;
69              
70             my @subs_to_export
71 8         38 = $export_class && $EXPORT_CLASS{$export_class}
72 12 100 66     74 ? (@{ $EXPORT_CLASS{$export_class} }, @subs)
73             : @subs;
74              
75 4     4   25 no strict 'refs'; ## no critic;
  4         7  
  4         1082  
76 12         23 for my $sub_to_export (@subs_to_export) {
77 94         145 _install_sub_from_class($this_class, $sub_to_export => $caller_class);
78             }
79              
80              
81 12         30 my %INSTALL_METHOD = (
82             DESTROY => _create_destroy_method($caller_class),
83             __setattr__ => _create_setattr($caller_class),
84             __getattr__ => _create_getattr($caller_class),
85             __hasattr__ => _create_hasattr($caller_class),
86             );
87 12 100       39 if ($options->{'-new'}) {
88 10         18 $INSTALL_METHOD{'new'} = _create_constructor($caller_class);
89             }
90              
91 12         44 while (my ($method_name, $method_ref) = each %INSTALL_METHOD) {
92 58         98 _install_sub_from_coderef($method_ref => $caller_class, $method_name);
93             }
94              
95 12         24 $PROPERTIES_FOR{$caller_class} = {};
96              
97 12         576 return;
98             }
99              
100             sub _install_sub_from_class {
101 94     94   105 my ($pkg_from, $sub_name, $pkg_to) = @_;
102 94         327 my $from = join q{::}, ($pkg_from, $sub_name);
103 94         115 my $to = join q{::}, ($pkg_to, $sub_name);
104              
105 4     4   21 no strict 'refs'; ## no critic
  4         5  
  4         334  
106 94         77 *{$to} = *{$from};
  94         408  
  94         169  
107              
108 94         171 return;
109             }
110              
111             sub _install_sub_from_coderef {
112 144     144   192 my ($coderef, $pkg_to, $sub_name) = @_;
113 144         213 my $to = join q{::}, ($pkg_to, $sub_name);
114              
115 4     4   17 no strict 'refs'; ## no critic
  4         6  
  4         106  
116 4     4   16 no warnings 'redefine'; ## no critic
  4         5  
  4         1319  
117 144         132 *{$to} = $coderef;
  144         605  
118              
119 144         412 return;
120             }
121              
122             sub _create_setattr {
123 12     12   22 my ($caller_class) = @_;
124 12         19 my $options = $OPTIONS_FOR{$caller_class};
125              
126             return sub {
127 17     17   31 my ($self, $attribute, $value) = @_;
128 17         36 my $property_key
129             = $INTERNAL_ATTR_NOISE . $attribute . $INTERNAL_ATTR_NOISE;
130 17         49 my $properties = __PACKAGE__->properties_for_class($self);
131 17 100       157 return if not $properties->{$attribute};
132 16         60 $self->{$property_key} = $value;
133 16         60 return 1;
134             }
135 12         63 }
136              
137             sub _create_getattr {
138 12     12   17 my ($caller_class) = @_;
139              
140             return sub {
141 1     1   4 my ($self, $attribute) = @_;
142 1         3 my $property_key
143             = $INTERNAL_ATTR_NOISE . $attribute . $INTERNAL_ATTR_NOISE;
144 1         6 my $properties = __PACKAGE__->properties_for_class($self);
145 1 50       6 return if not $properties->{$attribute};
146 1         10 return $self->{$property_key};
147             }
148 12         63 }
149              
150             sub _create_hasattr {
151 12     12   14 my ($caller_class) = @_;
152              
153             # For some reason, perlcritic thinks 'return sub {)'
154             # is ProhibitMixedBooleanOperators, so need no critic here.
155             return sub { ## no critic
156 34     34   2387 my ($self, $attribute) = @_;
157 34         44 my $ref_self = ref $self;
158              
159 34         31 my $class;
160 34 50       53 if ($ref_self) {
161 34         43 $class = $ref_self;
162             }
163             else {
164 0         0 $class = $self;
165             }
166              
167 4     4   20 no strict 'refs'; ## no critic;
  4         7  
  4         902  
168 34         34 my @isa = @{ "${class}::ISA" };
  34         312  
169 34         45 my $has_property = 0;
170              
171             ISA:
172 34         50 for my $isa ($class, @isa) {
173 43 100 66     230 if ($PROPERTIES_FOR{$isa} && $PROPERTIES_FOR{$isa}{$attribute}) {
174 16         22 $has_property = 1;
175 16         26 last ISA;
176             }
177             }
178              
179 34 100       121 return if not $has_property;
180 16         59 return 1;
181             }
182 12         90 }
183              
184              
185             sub _create_constructor {
186 10     10   13 my ($caller_class) = @_;
187 10         15 my $options = $OPTIONS_FOR{$caller_class};
188              
189             return sub {
190 18     18   6145 my ($class, $options_ref) = @_;
191 18   100     70 $options_ref ||= {};
192              
193 18         50 my $self = { };
194 18         45 bless $self, $class;
195              
196 50         208 OPTION:
197 18         66 while (my ($opt_key, $opt_value) = each %{$options_ref}) {
198              
199 32 100       69 if ($self->__hasattr__($opt_key)) {
200 15         42 $self->__setattr__($opt_key, $opt_value);
201             }
202             }
203              
204 4     4   18 no strict 'refs'; ## no critic
  4         4  
  4         701  
205 18 100       22 if (my $build_ref = *{ $class . '::BUILD' }{CODE}) { ## no critic
  18         91  
206 12         36 $Carp::CallLevel++; ## no critic
207 12         43 my $ret = $build_ref->($self, $options_ref);
208 12         153 $Carp::CallLevel--; ## no critic
209 12 100 66     63 if ($options->{'-rebuild'} && ref $ret) {
210 4         5 $self = $ret;
211             }
212             }
213              
214 18         58 return $self;
215             }
216 10         45 }
217              
218             sub properties_for_class {
219 23     23 1 25527 my ($self, $class) = @_;
220 23   66     74 $class = ref $class || $class; ## no critic
221              
222 23         31 my %class_properties;
223              
224             my @isa_for_class;
225             {
226 4     4   21 no strict 'refs'; ## no critic
  4         12  
  4         557  
  23         28  
227 23         21 @isa_for_class = @{ $class . '::ISA' };
  23         87  
228             }
229              
230 23         139 for my $parent ($class, @isa_for_class) {
231 41         42 for my $parent_property (keys %{ $PROPERTIES_FOR{$parent} }) {
  41         179  
232 196         400 $class_properties{$parent_property} = 1;
233             }
234             }
235              
236 23         115 return \%class_properties;
237             }
238              
239             sub _create_destroy_method {
240 12     12   17 my ($caller_class) = @_;
241              
242             return sub {
243 16     16   3213 my ($self) = @_;
244             #my $properties_ref =$PROPERTIES_FOR{$caller_class};
245             #undef %{$properties_ref};
246             #delete $PROPERTIES_FOR{$caller_class};
247              
248 4     4   19 no strict 'refs'; ## no critic
  4         7  
  4         117  
249 4     4   19 no warnings 'once'; ## no critic
  4         7  
  4         550  
250 16 100       19 if (my $demolish_ref = *{$caller_class.'::DEMOLISH'}{CODE}) { ## no critic
  16         75  
251 2         9 $demolish_ref->($self);
252             }
253              
254 16         381 return;
255             }
256 12         91 }
257              
258             sub property (@) { ## no critic
259 44     44 1 811 my ($property, $isa) = @_;
260 44 100       88 return if not $property;
261              
262 43         64 my $caller_class = caller;
263 43         55 my $set_property = "set_$property";
264              
265              
266 4     4   20 no strict 'refs'; ## no critic
  4         5  
  4         4794  
267 43 100       46 if (not *{ $caller_class . "::$property" }{CODE}) {
  43         217  
268 42         69 my $get_accessor = _create_get_accessor($caller_class, $property, $isa);
269 42         73 _install_sub_from_coderef($get_accessor => $caller_class, $property);
270             }
271              
272 43 100       44 if (not *{ $caller_class . "::$set_property" }{CODE}) {
  43         215  
273 42         74 my $set_accessor = _create_set_accessor($caller_class, $property, $isa);
274 42         76 _install_sub_from_coderef($set_accessor => $caller_class, $set_property);
275             }
276              
277 43         85 $PROPERTIES_FOR{$caller_class}->{$property} = 1;
278              
279 43         103 return;
280             }
281              
282             sub after_property_get (@&) { ## no critic
283 1     1 1 3 my ($property, $func_ref) = @_;
284 1         1 my $caller_class = caller;
285              
286 1         2 _install_sub_from_coderef($func_ref => $caller_class, $property);
287              
288 1         2 return;
289             }
290              
291             sub after_property_set (@&) { ## no critic
292 1     1 1 2 my ($property, $func_ref) = @_;
293 1         1 my $caller_class = caller;
294 1         2 my $set_property = "set_$property";
295              
296 1         3 _install_sub_from_coderef($func_ref => $caller_class, $set_property);
297              
298 1         2 return;
299             }
300              
301             sub _create_get_accessor {
302 42     42   60 my ($caller_class, $property, $isa) = @_;
303 42         60 my $options = $OPTIONS_FOR{$caller_class};
304 42         64 my $property_key
305             = $INTERNAL_ATTR_NOISE . $property . $INTERNAL_ATTR_NOISE;
306              
307 42 100       274 if ($options->{'-chained'}) {
308             return sub {
309 12     12   505 my $self = shift;
310 12 100       29 if (@_) {
311 4         8 my $set_property = "set_$property";
312 4         10 $self->$set_property($_[0]);
313 4         12 return $self;
314             }
315 8 50       21 if (!exists $self->{$property_key}) {
316 0 0       0 $self->{$property_key} =
317             ref $isa eq 'CODE'
318             ? $isa->($self)
319             : $isa;
320             }
321            
322 8         45 return $self->{$property_key};
323 4         17 };
324             }
325             else {
326             return sub {
327 47     47   17834 my $self = shift;
328              
329 47 100       150 if (@_) {
330 1         10 require Carp;
331 1         211 Carp::croak("You tried to set a value with $property(). Did "
332             ."you mean set_$property() ?");
333             }
334              
335 46 100       296 if (!exists $self->{$property_key}) {
336 19 100       122 $self->{$property_key} =
337             ref $isa eq 'CODE'
338             ? $isa->($self)
339             : $isa;
340             }
341            
342 46         316 return $self->{$property_key};
343 38         349 };
344             }
345             }
346              
347             sub _create_set_accessor {
348 42     42   52 my ($caller_class, $property) = @_;
349 42         59 my $options = $OPTIONS_FOR{$caller_class};
350 42         66 my $property_key
351             = $INTERNAL_ATTR_NOISE . $property . $INTERNAL_ATTR_NOISE;
352              
353 42 100       106 if ($options->{'-chained'}) {
354            
355             return sub {
356 8     8   13 my ($self, $value ) = @_;
357 8         21 $self->{$property_key} = $value;
358 8         25 return $self; # <-- this is the chained part.
359             }
360 4         14 }
361             else {
362             return sub {
363 4     4   16 my ($self, $value) = @_;
364 4         13 $self->{$property_key} = $value;
365 4         10 return;
366             }
367 38         170 }
368             }
369              
370             1;
371              
372             __END__