File Coverage

blib/lib/Object/Botox.pm
Criterion Covered Total %
statement 37 37 100.0
branch 8 8 100.0
condition 8 9 88.8
subroutine 11 11 100.0
pod 1 1 100.0
total 65 66 98.4


line stmt bran cond sub pod time code
1             package Object::Botox;
2              
3 4     4   112462 use 5.008;
  4         18  
  4         178  
4 4     4   24 use strict;
  4         10  
  4         157  
5 4     4   42 use warnings;
  4         10  
  4         327  
6              
7              
8             =head1 NAME
9              
10             Object::Botox - simple object constructor with accessor, prototyping and default-settings of inheritanced values.
11              
12             =head1 VERSION
13              
14             Version 1.15
15              
16             =cut
17              
18             our $VERSION = '1.15';
19             $VERSION = eval $VERSION;
20              
21             =head1 SYNOPSIS
22              
23             Object::Botox writed for easy object creation by default constructor and support managment properties,
24             inherited by children of prototyping class.
25              
26             package Parent;
27             use Botox; # yes, we now are got |new| constructor
28            
29             # default properties for ANY object of `Parent` class:
30             # prop1_ro ISA 'write-protected' && prop2 ISA 'public'
31             # and seting default value for each other
32            
33             # strictly named constant PROTOTYPE !
34             use constant PROTOTYPE => { 'prop1_ro' => 1 , 'prop2' => 'abcde' };
35            
36             =head1 DESCRIPTION
37              
38             Object::Botox - simple constructor and properties prototyper whith checking of properties existans.
39              
40             To create parent module:
41            
42             package Parent;
43              
44             use Botox;
45            
46             # strictly named constant PROTOTYPE !
47             use constant PROTOTYPE => {
48             'prop1_ro' => 1 ,
49             'prop2' => 'abcde'
50             };
51            
52             sub show_prop1{ # It`s poinlessly - indeed property IS A accessor itself
53             my ( $self ) = @_;
54             return $self->prop1;
55             }
56            
57             sub set_prop1{ # It`s NEEDED for RO aka protected on write property
58             my ( $self, $value ) = @_;
59             $self->prop1($value);
60             }
61            
62             sub parent_sub{ # It`s class method itself
63             my $self = shift;
64             return $self->prop1;
65             }
66             1;
67              
68             after that we are create instanse:
69              
70             package main;
71             use Data::Dumper;
72            
73             # change default value for prop1
74             my $foo = new Parent( { prop1 => 888888 } );
75            
76             print Dumper($foo);
77              
78             outputs get to us:
79              
80             $VAR1 = bless( {
81             'Parent::prop1' => 888888,
82             'Parent::prop2' => 'abcde'
83             }, 'Parent' );
84              
85             properties may have _rw[default] or _ro acess mode and inheritated.
86              
87             eval{ $foo->prop1(-23) };
88             print $@."\n";
89            
90             output somthing like this:
91              
92             Can`t change RO properties |prop1| to |-23| in object Parent from main at ./test_more.t line 84
93              
94             to deal (write to) with this properties we are must create accessor .
95              
96             Also all of properties are inheritanced.
97              
98             package Child;
99             use base 'Parent';
100              
101             use constant PROTOTYPE => {
102             'prop1' => 48,
103             'prop5' => 55,
104             'prop8_ro' => 'tetete'
105             };
106             1;
107              
108             give to us something like this
109              
110             $VAR1 = bless( {
111             'Child::prop5' => 55,
112             'Child::prop2' => 'abcde',
113             'Child::prop1' => 48,
114             'Child::prop8' => 'tetete'
115             }, 'Child' );
116              
117             Chainings - all setter return $self in success, so its chained
118              
119             $baz->prop1(88)->prop2('loreum ipsum');
120              
121             =head1 EXPORT
122              
123             new() method by default
124              
125             =cut
126              
127 4     4   23 use constant 1.01;
  4         96  
  4         129  
128 4     4   3685 use MRO::Compat qw( get_linear_isa ); # mro::* interface compatibility for Perls < 5.9.5
  4         15628  
  4         134  
129 4     4   3341 use autouse 'Carp' => qw( croak carp );
  4         3114  
  4         29  
130              
131             my ( $create_accessor, $prototyping, $setup, $pre_set );
132              
133             my %properties_cache; # inside-out styled chache
134              
135             my $err_text = [
136             qq(Can`t change RO property |%s| to |%s| in object %s from %s),
137             qq(Haven`t property |%s|, can't set to |%s| in object %s from %s),
138             qq(Name |%s| reserved as property, but subroutine named |%s| in class %s was founded, new\(\) method from %s aborted),
139             qq(Odd number of elements in list),
140             qq(Only list or anonymous hash are alowed in new\(\) method in object %s)
141             ];
142              
143             =head1 SUBROUTINES/METHODS
144              
145             =head2 new
146              
147             new() - create object (on hashref-base) by prototype and initiate it from args
148              
149             =cut
150              
151             sub new{
152 9     9 1 2168 my $invocant = shift;
153 9   66     64 my $self = bless( {}, ref $invocant || $invocant );
154 9 100       53 exists $properties_cache{ ref $self } ? $pre_set->( $self ) : $prototyping->( $self );
155 8 100       29 $setup->( $self, @_ ) if @_;
156 7         28 return $self;
157             }
158              
159              
160             =begin comment
161              
162             import(protected)
163            
164             Parameters:
165             @_ - calling args
166             Returns:
167             void
168             Explain:
169             - implant to caller new() constructor (I don`t think is it need to rename)
170              
171             =end comment
172              
173             =cut
174              
175             sub import{
176 4     4   1008 no strict 'refs';
  4         8  
  4         1819  
177 8     8   307 *{+caller().'::new'} = \&new; # fix 'Use of "caller" without parentheses is ambiguous' warning
  8         249  
178            
179             }
180              
181             =begin comment
182              
183             pre_set (private)
184              
185             initiate object with proto-properites, if we are have some object of this class in cache
186              
187             Parameters:
188             $self - object
189             Returns:
190             void
191             Explain:
192             get the cached properties and initiate by this values
193             we are always have all accessors
194              
195             =end comment
196              
197             =cut
198              
199              
200             $pre_set = sub{
201              
202             my $self = shift;
203             while ( my ($key, $value) = each %{$properties_cache{ ref $self }} ){
204             $self->$key($value);
205             };
206              
207             };
208              
209             =begin comment
210              
211             prototyping (private)
212              
213             construct object by available proto-properties, declared in itself or in parents
214             Parameters:
215             $self - object
216             Returns:
217             void
218             Explain:
219             walk thrue object tree, begin by object themself and build it by proto
220              
221             =end comment
222              
223             =cut
224              
225              
226             $prototyping = sub{
227            
228             my $self = shift;
229             my $class_list = mro::get_linear_isa( ref $self );
230             # it`s for exist properies ( we are allow redefine, keeping highest )
231             my %seen_prop;
232              
233             foreach my $class ( @$class_list ){
234            
235             # next if haven`t prototype
236             next unless ( $constant::declared{$class."::PROTOTYPE"} );
237            
238             my $proto = $class->PROTOTYPE();
239             next unless ( ref $proto eq 'HASH' );
240              
241             # or if we are having prototype - use it !
242             for ( reverse keys %$proto ) { # anyway we are need some order, isn`t it?
243            
244             my ( $field, $ro ) = /^(.+)_(r[ow])$/ ? ( $1, $2 ) : $_ ;
245             next if ( exists $seen_prop{$field} );
246             $seen_prop{$field} = $proto->{$_}; # for caching
247            
248             $create_accessor->( $self, $field, defined $ro && $ro eq 'ro' );
249             $self->$field( $proto->{$_} );
250            
251             # need check property are REALY setted, or user defined same named subroutine, I think
252             unless ( exists $self->{ (ref $self).'::'.$field} ){
253             croak sprintf $err_text->[2], $field, $field, ref $self, caller(1);
254             }
255            
256             }
257             }
258            
259             $properties_cache{ ref $self } = \%seen_prop; # for caching
260             };
261              
262             =begin comment
263              
264             create_accessor (private)
265              
266             create accessors for properites
267             Parameters:
268             $class - object class
269             $field - property name
270             $ro - property type : [ 1|undef ]
271             Returns:
272             void
273              
274             =end comment
275              
276             =cut
277              
278             $create_accessor = sub{
279             my $class = ref shift;
280             my ( $field, $ro ) = @_ ;
281            
282             my $slot = "$class\::$field"; # inject sub to invocant package space
283 4     4   25 no strict 'refs'; # So symbolic ref to typeglob works.
  4         6  
  4         1502  
284             return if ( *$slot{CODE} ); # don`t redefine ours closures
285            
286             *$slot = sub { # or create closures
287 77     77   13495 my $self = shift;
288 77 100       521 return $self->{$slot} unless ( @_ );
289 40 100 100     228 if ( $ro && !( caller eq ref $self || caller eq __PACKAGE__ ) ){
      100        
290 3         628 croak sprintf $err_text->[0], $field, shift, ref $self, caller;
291             }
292 37         146 $self->{$slot} = shift;
293 37         86 return $self; # yap! for chaining
294             };
295              
296             };
297              
298             =begin comment
299              
300             setup (private)
301              
302             fill object properties by default values
303             Parameters:
304             $self - object
305             @_ - properties as list or hashref:
306             (prop1=>aaa,prop2=>bbb) AND ({prop1=>aaa,prop2=>bbb}) ARE allowed
307             Returns:
308             void
309              
310             =end comment
311              
312             =cut
313              
314             $setup = sub{
315             my $self = shift;
316             my %prop;
317            
318             if ( ref $_[0] eq 'HASH' ){
319             %prop = %{$_[0]};
320             }
321             elsif ( ! ref $_[0] ) {
322             unless ( $#_ % 2 ) {
323             # so, if list are odd whe are have many troubless,
324             # but for support some way as perl 'Odd number at anonimous hash'
325             carp sprintf $err_text->[3], caller(1);
326             push @_, undef;
327             }
328             %prop = @_ ;
329             }
330             else {
331             croak sprintf $err_text->[4], ref $self, caller(1);
332             }
333              
334             while ( my ($key, $value) = each %prop ){
335             # if realy haven`t property in PROTOTYPE
336             unless ( exists ${$properties_cache{ ref $self }}{$key} ) {
337             croak sprintf $err_text->[1], $key, $value, ref $self, caller(1);
338             }
339             $self->$key( $value );
340             }
341              
342             };
343              
344             =head1 SEE ALSO
345              
346             Moose, Mouse, Class::Accessor, Class::XSAccessor
347              
348             =head1 AUTHOR
349              
350             Meettya, C<< >>
351              
352             =head1 BUGS
353              
354             Please report any bugs or feature requests to C, or through
355             the web interface at L. I will be notified, and then you'll
356             automatically be notified of progress on your bug as I make changes.
357              
358              
359             =head1 SUPPORT
360              
361             You can find documentation for this module with the perldoc command.
362              
363             perldoc Object::Botox
364              
365              
366             You can also look for information at:
367              
368             =over 4
369              
370             =item * RT: CPAN's request tracker (report bugs here)
371              
372             L
373              
374             =item * AnnoCPAN: Annotated CPAN documentation
375              
376             L
377              
378             =item * CPAN Ratings
379              
380             L
381              
382             =item * Search CPAN
383              
384             L
385              
386             =back
387              
388              
389             =head1 ACKNOWLEDGEMENTS
390              
391              
392             =head1 LICENSE AND COPYRIGHT
393              
394             Copyright 2011 Meettya.
395              
396             This program is free software; you can redistribute it and/or modify it
397             under the terms of either: the GNU General Public License as published
398             by the Free Software Foundation; or the Artistic License.
399              
400             See http://dev.perl.org/licenses/ for more information.
401              
402              
403             =cut
404              
405             1; # End of Object::Botox