File Coverage

blib/lib/Abstract/Meta/Class.pm
Criterion Covered Total %
statement 192 197 97.4
branch 51 58 87.9
condition 13 16 81.2
subroutine 52 54 96.3
pod 34 34 100.0
total 342 359 95.2


line stmt bran cond sub pod time code
1             package Abstract::Meta::Class;
2              
3 5     5   120903 use strict;
  5         12  
  5         191  
4 5     5   106 use warnings;
  5         9  
  5         154  
5              
6 5     5   28 use base 'Exporter';
  5         14  
  5         609  
7 5     5   27 use vars qw(@EXPORT_OK %EXPORT_TAGS);
  5         11  
  5         343  
8 5     5   29 use Carp 'confess';
  5         9  
  5         367  
9 5     5   23 use vars qw($VERSION);
  5         9  
  5         438  
10              
11             $VERSION = 0.11;
12              
13             @EXPORT_OK = qw(has new apply_contructor_parameter install_meta_class abstract abstract_class storage_type);
14             %EXPORT_TAGS = (all => \@EXPORT_OK, has => ['has', 'install_meta_class', 'abstract', 'abstract_class', 'storage_type']);
15              
16 5     5   3126 use Abstract::Meta::Attribute;
  5         17  
  5         167  
17 5     5   109 use Abstract::Meta::Attribute::Method;
  5         11  
  5         6849  
18              
19             =head1 NAME
20              
21             Abstract::Meta::Class - Simple meta object protocol implementation.
22              
23             =head1 SYNOPSIS
24              
25             package Dummy;
26              
27             use Abstract::Meta::Class ':all';
28            
29            
30             has '$.attr1' => (default => 0);
31             has '%.attrs2' => (default => {a => 1, b => 3}, item_accessor => 'attr2');
32             has '@.atts3' => (default => [1, 2, 3], required => 1, item_accessor => 'attr3');
33             has '&.att3' => (required => 1);
34             has '$.att4' => (default => sub { 'stuff' } , required => 1);
35              
36              
37             my $dummt = Dummy->new(
38             att3 => 3,
39             );
40              
41             use Dummy;
42              
43             my $obj = Dummy->new(attr3 => sub {});
44             my $attr1 = $obj->attr1; #0
45             $obj->set_attr1(1);
46             $obj->attr2('c', 4);
47             $obj->attrs2 #{a => 1, b => 3. c => 4};
48             my $val_a = $obj->attr2('a');
49             my $item_1 = $obj->attr3(1);
50             $obj->count_attrs3();
51             $obj->push_attrs3(4);
52              
53              
54              
55             =head1 DESCRIPTION
56              
57             Meta object protocol implementation,
58              
59             =head2 hash/array storage type
60              
61             To speed up bless time as well optimise memory usage you can use Array storage type.
62             (Hash is the default storage type)
63              
64             package Dummy;
65              
66             use Abstract::Meta::Class ':all';
67             storage_type 'Array';
68            
69             has '$.attr1' => (default => 0);
70             has '%.attrs2' => (default => {a => 1, b => 3}, item_accessor => 'attr2');
71             has '@.attrs3' => (default => [1, 2, 3], required => 1, item_accessor => 'attr3');
72             has '&.attr4' => (required => 1);
73             has '$.attr5';
74             has '$.attr6' => (default => sub { 'stuff' } , required => 1);
75              
76              
77             my $dummy = Dummy->new(
78             attr4 => sub {},
79             );
80            
81             use Data::Dumper;
82             warn Dumper $dummy;
83             # bless [0, {a =>1,b => 3}, [1,2,3],sub{},undef,sub {}], 'Dummy'
84              
85             =head2 simple validation and default values
86              
87             package Dummy;
88              
89             use Abstract::Meta::Class ':all';
90              
91             has '$.attr1' => (default => 0);
92             has '&.att3' => (required => 1);
93              
94             use Dummy;
95              
96             my $obj = Dummy->new; #dies - att3 required
97              
98              
99             =head2 utility methods for an array type
100              
101             While specyfing array type of attribute
102             the following methods are added (count || push || pop || shift || unshift)_accessor.
103              
104             package Dummy;
105              
106             use Abstract::Meta::Class ':all';
107              
108             has '@.array' => (item_accessor => 'array_item');
109              
110              
111             use Dummy;
112              
113             my $obj = Dummy->new;
114              
115             $obj->count_array();
116             $obj->push_array(1);
117             my $x = $obj->array_item(0);
118             my $y = $obj->pop_array;
119              
120             #NOTE scalar, array context sensitive
121             my $array_ref = $obj->array;
122             my @array = $obj->array;
123              
124              
125             =head2 item accessor method for complex types
126              
127             While specyfing an array or a hash type of attribute then
128             you may specify item_accessor for get/set value by hash key or array index.
129              
130              
131             package Dummy;
132              
133             use Abstract::Meta::Class ':all';
134              
135             has '%.hash' => (item_accessor => 'hash_item');
136              
137             use Dummy;
138              
139             my $obj = Dummy->new;
140             $obj->hash_item('key1', 'val1');
141             $obj->hash_item('key2', 'val2');
142             my $val = $obj->hash_item('key1');
143              
144             #NOTE scalar, array context sensitive
145             my $hash_ref = $obj->hash;
146             my %hash = $obj->hash;
147              
148              
149             =head2 perl types validation
150              
151             Dy default all complex types are validated against its definition.
152              
153             package Dummy;
154             use Abstract::Meta::Class ':all';
155              
156             has '%.hash' => (item_accessor => 'hash_item');
157             has '@.array' => (item_accessor => 'array_item');
158              
159              
160             use Dummy;
161              
162             my $obj = Dummy->new(array => {}, hash => []) #dies incompatible types.
163              
164              
165             =head2 associations
166              
167             This module handles different types of associations(to one, to many, to many ordered).
168             You may also use bidirectional association by using the_other_end option,
169              
170             NOTE: When using the_other_end automatic association/deassociation happens,
171             celanup method is installed.
172              
173             package Class;
174              
175             use Abstract::Meta::Class ':all';
176              
177             has '$.to_one' => (associated_class => 'AssociatedClass');
178             has '@.ordered' => (associated_class => 'AssociatedClass');
179             has '%.to_many' => (associated_class => 'AssociatedClass', item_accessor => 'many', index_by => 'id');
180              
181              
182             use Class;
183             use AssociatedClass;
184              
185             my $obj1 = Class->new(to_one => AssociatedClass->new);
186              
187             my $obj2 = Class->new(ordered => [AssociatedClass->new]);
188              
189             # NOTE: context sensitive (scalar, array)
190             my @association_objs = $obj2->ordered;
191             my @array_ref = $obj2->ordered;
192              
193             my $obj3 = Class->new(to_many => [AssociatedClass->new(id =>'001'), AssociatedClass->new(id =>'002')]);
194             my $association_obj = $obj3->many('002);
195              
196             # NOTE: context sensitive (scalar, array)
197             my @association_objs = values %{$obj3->to_many};
198             my $hash_ref = $obj3->to_many;
199              
200              
201             - bidirectional associations (the_other_end attribute)
202              
203             package Master;
204              
205             use Abstract::Meta::Class ':all';
206              
207             has '$.name';
208             has '%.details' => (associated_class => 'Detail', the_other_end => 'master', item_accessor => 'detail', index_by => 'id');
209              
210              
211             package Detail;
212              
213             use Abstract::Meta::Class ':all';
214              
215             has '$.id' => (required => 1);
216             has '$.master' => (
217             associated_class => 'Master',
218             the_other_end => 'details'
219             );
220              
221              
222             use Master;
223             use Detail;
224              
225             my @details = (
226             Detail->new(id => 1),
227             Detail->new(id => 2),
228             Detail->new(id => 3),
229             );
230              
231             my $master = Master->new(name => 'foo', details => [@details]);
232             print $details[0]->master->name;
233              
234             - while using an array/hash association storage remove_ | add_ are added.
235             $master->add_details(Detail->new(id => 4),);
236             $master->remove_details($details[0]);
237             #cleanup method is added to class, that deassociates all bidirectional associations
238              
239              
240             =head2 decorators
241              
242             ....- on_validate
243              
244             - on_change
245              
246             - on_read
247              
248             - initialise_method
249              
250             package Triggers;
251              
252             use Abstract::Meta::Class ':all';
253              
254             has '@.y' => (
255             on_change => sub {
256             my ($self, $attribute_name, $scope, $value_ref, $index) = @_;
257             # scope -> mutator, item_accessor
258             ... do some stuff
259              
260             # process further in standard way by returning true
261             $self;
262             },
263             # replaces standard read
264             on_read => sub {
265             my ($self, $attr_name, $scope, $index)
266             #scope can be: item_accessor, accessor
267             ...
268             #return requested value
269             },
270             item_accessor => 'y_item'
271             );
272              
273             use Triggers;
274              
275             my $obj = Triggers->new(y => [1,2,3]);
276              
277             - add hoc decorators
278              
279             package Class;
280             use Abstract::Meta::Class ':all';
281              
282             has '%.attrs' => (item_accessor => 'attr');
283              
284             my $attr = DynamicInterceptor->meta->attribute('attrs');
285             my $obj = DynamicInterceptor->new(attrs => {a => 1, b => 2});
286             my $a = $obj->attr('a');
287             my %hook_access_log;
288             my $ncode_ref = sub {
289             my ($self, $attribute, $scope, $key) = @_;
290             #do some stuff
291             # or
292             if ($scope eq 'accessor') {
293             return $values;
294             } else {
295             return $values->{$key};
296             }
297              
298             };
299              
300              
301             $attr->set_on_read($ncode_ref);
302             # from now it will apply to Class::attrs calls.
303              
304             my $a = $obj->attr('a');
305              
306             =head2 abstract methods/classes
307              
308             package BaseClass;
309              
310             use Abstract::Meta::Class ':all';
311              
312             has '$.attr1';
313             abstract => 'method1';
314              
315              
316             package Class;
317              
318             use base 'BaseClass';
319             sub method1 {};
320              
321             use Class;
322              
323             my $obj = BaseClass->new;
324              
325              
326             # abstract classes
327              
328             package InterfaceA;
329              
330             use Abstract::Meta::Class ':all';
331              
332             abstract_class;
333             abstract => 'method1';
334             abstract => 'method2';
335              
336              
337             package ClassA;
338              
339             use base 'InterfaceA';
340              
341             sub method1 {};
342             sub method2 {};
343              
344             use Class;
345              
346             my $classA = Class->new;
347              
348              
349             package Class;
350              
351             use Abstract::Meta::Class ':all';
352              
353             has 'attr1';
354             has 'interface_attr' => (associated_class => 'InterfaceA', required => 1);
355              
356              
357             use Class;
358              
359             my $obj = Class->new(interface_attr => $classA);
360              
361              
362             =head2 external attributes storage
363              
364             You may want store attributes values outside the blessed reference, then you may
365             use transistent keyword (Inside Out Objects)
366              
367             package Transistent;
368             use Abstract::Meta::Class ':all';
369              
370             has '$.attr1';
371             has '$.x' => (required => 1);
372             has '$.t' => (transistent => 1);
373             has '%.th' => (transistent => 1);
374             has '@.ta' => (transistent => 1);
375              
376             use Transistent;
377              
378             my $obj = Transistent->new(attr1 => 1, x => 2, t => 3, th => {a =>1}, ta => [1,2,3]);
379             use Data::Dumper;
380             print Dumper $obj;
381              
382             Cleanup and DESTORY methods are added to class, that delete externally stored attributes.
383              
384              
385             =head2 METHODS
386              
387             =over
388              
389             =item new
390              
391             =cut
392              
393             sub new {
394 80     80 1 9821 my $class = shift;
395 80         222 my $self = bless {}, $class;
396 80         158 unshift @_, $self;
397 80         151 &apply_contructor_parameters;
398             }
399              
400              
401             =item install_cleanup
402              
403             Install cleanup method
404              
405             =cut
406              
407             sub install_cleanup {
408 7     7 1 13 my ($self) = @_;
409 7         8 my $attributes;
410 7 100       20 return if $self->has_cleanup_method;
411             add_method($self->associated_class, 'cleanup' , sub {
412 3     3   7 my $this = shift;
413 3         4 my $has_transistent;
414 3 50 33     25 my $attributes ||= $self ? $self->all_attributes : [];
415 3         9 for my $attribute (@$attributes) {
416 10 50       23 $attribute or next;
417 10 100       28 $has_transistent = 1 if($attribute->transistent);
418 10 100       27 if($attribute->the_other_end) {
419 1         5 $attribute->deassociate($this);
420 1         4 my $accessor = "set_" . $attribute->accessor;
421 1         4 $this->$accessor(undef);
422             }
423             }
424 3 100       20 Abstract::Meta::Attribute::Method::delete_object($this) if $has_transistent;
425 5         11 });
426 5         25 $self->set_cleanup_method(1);
427             }
428              
429              
430             =item install_destructor
431              
432             Install destructor method
433              
434             =cut
435              
436             sub install_destructor {
437 3     3 1 5 my ($self) = @_;
438 3 100       7 return if $self->has_destory_method;
439             add_method($self->associated_class, 'DESTROY' , sub {
440 1     1   415 my $this = shift;
441 1         3 $this->cleanup;
442 1         4 $this;
443 1         3 });
444 1         10 $self->set_destroy_method(1);
445             }
446              
447              
448              
449             =item install_constructor
450              
451             Install constructor
452              
453             =cut
454              
455             sub install_constructor {
456 2     2 1 4 my ($self) = @_;
457             add_method($self->associated_class, 'new' ,
458             $self->storage_type eq 'Array' ?
459             sub {
460 0     0   0 my $class = shift;
461 0         0 my $this = bless [], $class;
462 0         0 unshift @_, $this;
463 0         0 &apply_contructor_parameters;
464             }: sub {
465 1     1   6 my $class = shift;
466 1         11 my $this = bless {}, $class;
467 1         3 unshift @_, $this;
468 1         2 &apply_contructor_parameters;
469 2 100       6 });
470             }
471              
472              
473             =item apply_contructor_parameters
474              
475             Applies constructor parameters.
476              
477             =cut
478              
479             {
480             sub apply_contructor_parameters {
481 81     81 1 218 my ($self, @args) = @_;
482 81         97 my $mutator;
483 81         124 my $class = ref($self);
484 81         109 eval {
485 81         260 for (my $i = 0; $i < $#args; $i += 2) {
486 132         324 $mutator = "set_" . $args[$i];
487 132         456 $self->$mutator($args[$i + 1]);
488             }
489             };
490            
491 81 100       1878 if ($@) {
492 6 50       33 confess "unknown attribute " . ref($self) ."::" . $mutator
493             unless $self->can($mutator);
494 6         1065 confess $@
495             }
496            
497 75         188 my $meta = $self->meta;
498 75 100       2433 return $self if $self eq $meta;
499            
500 49         130 for my $attribute ($meta->constructor_attributes) {
501 63 100       219 if(! $attribute->get_value($self)) {
502 25 50       77 my $can = $self->can($attribute->mutator) or next;
503 25         78 $can->($self);
504             }
505             }
506            
507 44         121 my $initialise = $self->can($meta->initialise_method);
508 44 100       107 $initialise->($self) if $initialise;
509 44         218 $self;
510             }
511             }
512              
513             =item meta
514              
515             =cut
516              
517 26     26 1 47 sub meta { shift(); }
518              
519              
520             =item attributes
521              
522             Returns attributes for meta class
523              
524             =cut
525              
526 139 50   139 1 550 sub attributes { shift()->{'@.attributes'} || {};}
527              
528              
529             =item set_attributes
530              
531             Mutator sets attributes for the meta class
532              
533             =cut
534              
535 26     26 1 113 sub set_attributes { $_[0]->{'@.attributes'} = $_[1]; }
536              
537              
538              
539             =item has_cleanup_method
540              
541             Returns true if cleanup method was generated
542              
543             =cut
544              
545 7     7 1 23 sub has_cleanup_method { shift()->{'$.cleanup'};}
546              
547              
548             =item set_cleanup_method
549              
550             Sets clean up
551              
552             =cut
553              
554 5     5 1 21 sub set_cleanup_method { $_[0]->{'$.cleanup'} = $_[1]; }
555              
556              
557             =item has_destory_method
558              
559             Returns true is destroy method was generated
560              
561             =cut
562              
563 3     3 1 10 sub has_destory_method { shift()->{'$.destructor'};}
564              
565              
566             =item set_destroy_method
567              
568             Sets set_destructor flag.
569              
570             =cut
571              
572 1     1 1 3 sub set_destroy_method { $_[0]->{'$.destructor'} = $_[1]; }
573              
574              
575             =item initialise_method
576              
577             Returns initialise method's name default is 'initialise'
578              
579              
580             =cut
581              
582 44     44 1 267 sub initialise_method { shift()->{'$.initialise_method'};}
583              
584              
585             =item is_abstract
586              
587             Returns is class is an abstract class.
588              
589             =cut
590              
591 0     0 1 0 sub is_abstract{ shift()->{'$.abstract'};}
592              
593              
594              
595             =item set_abstract
596              
597             Set an abstract class flag.
598              
599             =cut
600              
601 1     1 1 2 sub set_abstract{ shift()->{'$.abstract'} = 1;}
602              
603              
604             =item set_initialise_method
605              
606             Mutator sets initialise_method for the meta class
607              
608             =cut
609              
610 27     27 1 119 sub set_initialise_method { $_[0]->{'$.initialise_method'} = $_[1]; }
611              
612              
613             =item associated_class
614              
615             Returns associated class name
616              
617             =cut
618              
619 315     315 1 1105 sub associated_class { shift()->{'$.associated_class'} }
620              
621              
622             =item set_associated_class
623              
624             Mutator sets associated class name
625              
626             =cut
627              
628 26     26 1 162 sub set_associated_class { $_[0]->{'$.associated_class'} = $_[1]; }
629              
630              
631              
632             =item all_attributes
633              
634             Returns all_attributes for all inherited meta classes
635              
636             =cut
637              
638             sub all_attributes {
639 83     83 1 106 my $self = shift;
640 83 100       157 if(my @super_classes = $self->super_classes) {
641 1         2 my %attributes;
642 1         3 foreach my $super (@super_classes) {
643 1 50       2 my $meta_class = meta_class($super) or next;
644 1         2 $attributes{$_->name} = $_ for @{$meta_class->all_attributes};
  1         5  
645             }
646 1         2 $attributes{$_->name} = $_ for @{$self->attributes};
  1         3  
647 1         7 return [values %attributes];
648             }
649 82         174 $self->attributes;
650             }
651              
652              
653             =item attribute
654              
655             Returns attribute object
656              
657             =cut
658              
659             sub attribute {
660 28     28 1 49 my ($self, $name) = @_;
661 28         50 my $attributes = $self->all_attributes;
662 28         43 my @result = (grep {$_->accessor eq $name} @$attributes);
  64         131  
663 28 100       129 @result ? $result[0] : undef;
664             }
665              
666              
667              
668              
669              
670             =item super_classes
671              
672             =cut
673              
674             sub super_classes {
675 83     83 1 94 my $self = shift;
676 5     5   106 no strict 'refs';
  5         10  
  5         3266  
677 83         146 my $class = $self->associated_class;
678 83         102 @{"${class}::ISA"};
  83         541  
679             }
680              
681              
682             {
683             my %meta;
684              
685             =item install_meta_class
686              
687             Adds class to meta repository.
688              
689             =cut
690              
691             sub install_meta_class {
692 26     26 1 57 my ($class) = @_;
693 26         163 $meta{$class} = __PACKAGE__->new(
694             associated_class => $class,
695             attributes => [],
696             initialise_method => 'initialise'
697             );
698 26     79   177 add_method($class, 'meta', sub{$meta{$class}});
  79         196  
699             }
700              
701              
702             =item meta_class
703              
704             Returns meta class object for passed in class name.
705              
706             =cut
707              
708             sub meta_class {
709 68     68 1 114 my ($class) = @_;
710 68 100       230 install_meta_class($class)unless $meta{$class};
711 68         141 $meta{$class};
712             }
713             }
714              
715              
716             =item add_attribute
717              
718             =cut
719              
720             sub add_attribute {
721 56     56 1 87 my ($self, $attribute) = @_;
722 56         141 $self->install_attribute_methods($attribute);
723 56         105 push @{$self->attributes}, $attribute;
  56         130  
724             }
725              
726              
727             =item attribute_class
728              
729             Returns meta attribute class
730              
731             =cut
732              
733 56     56 1 178 sub attribute_class { 'Abstract::Meta::Attribute' }
734              
735              
736             =item has
737              
738             Creates a meta attribute.
739              
740             Takes attribute name, and the following attribute options:
741             see also L
742              
743             =cut
744              
745             sub has {
746 56     56 1 9510 my $name = shift;
747 56         292 my $package = caller();
748 56         147 my $meta_class = meta_class($package);
749 56         311 my $attribute = $meta_class->attribute_class->new(name => $name, @_, class => $package, storage_type => $meta_class->storage_type);
750 56         220 $meta_class->add_attribute($attribute);
751 56 100 100     146 $meta_class->install_cleanup
752             if($attribute->transistent || $attribute->index_by);
753 56 100       148 $meta_class->install_destructor
754             if $attribute->transistent;
755 56         137 $attribute;
756             }
757              
758              
759             =item storage_type
760              
761             Sets storage type for the attributes.
762             allowed values are Array/Hash
763              
764             =cut
765              
766             sub storage_type {
767 59     59 1 789 my ($param) = @_;
768 59 100 100     769 return $param->{'$.storage_type'} ||= 'Hash'
769             if (ref($param));
770 1         3 my $type = $param;
771 1 50       11 confess "unknown storage type $type - should be Array or Hash"
772             unless($type =~ /Array|Hash/);
773 1         4 my $package = caller();
774 1         4 my $meta_class = meta_class($package);
775 1         3 $meta_class->{'$.storage_type'} = $type;
776 1         3 remove_method($meta_class->associated_class, 'new');
777 1         5 $meta_class->install_constructor();
778            
779             }
780              
781              
782             =item abstract
783              
784             Creates an abstract method
785              
786             =cut
787              
788             sub abstract {
789 1     1 1 2 my $name = shift;
790 1         2 my $package = caller();
791 1         4 my $meta_class = meta_class($package);
792 1         5 $meta_class->install_abstract_methods($name);
793             }
794              
795              
796              
797             =item abstract_class
798              
799             Creates an abstract method
800              
801             =cut
802              
803             sub abstract_class {
804 1     1 1 649 my $name = shift;
805 1         3 my $package = caller();
806 1         2 my $meta_class = meta_class($package);
807 1         5 $meta_class->set_abstract(1);
808 5     5   36 no warnings 'redefine';
  5         10  
  5         247  
809 5     5   22 no strict 'refs';
  5         9  
  5         2662  
810 1         9 *{"${package}::new"} = sub {
811 1     1   185 confess "Can't instantiate abstract class " . $package;
812 1         4 };
813             }
814              
815             =item install_abstract_methods
816              
817             =cut
818              
819             sub install_abstract_methods {
820 1     1 1 2 my ($self, $method_name) = @_;
821             add_method($self->associated_class, $method_name, sub {
822 1     1   765 confess $method_name . " is an abstract method";
823 1         4 });
824             }
825              
826              
827             =item install_attribute_methods
828              
829             Installs attribute methods.
830              
831             =cut
832              
833             sub install_attribute_methods {
834 58     58 1 83 my ($self, $attribute, $remove_existing_method) = @_;
835 58         226 my $accessor = $attribute->accessor;
836 58         123 foreach (qw(accessor mutator)) {
837 116         250 add_method($self->associated_class, $attribute->$_, $attribute->generate($_), $remove_existing_method);
838             }
839              
840 58         179 my $perl_type = $attribute->perl_type ;
841 58 100       144 if ($perl_type eq 'Array') {
842             add_method($self->associated_class, "${_}_$accessor", $attribute->generate("$_"), $remove_existing_method)
843 11         41 for qw(count push pop shift unshift);
844             }
845              
846 58 100       157 if (my $item_accessor = $attribute->item_accessor) {
847 11         29 add_method($self->associated_class, $item_accessor, $attribute->generate('item_accessor'), $remove_existing_method);
848             }
849            
850 58 100 100     337 if (($perl_type eq 'Array' || $perl_type eq 'Hash') && $attribute->associated_class) {
      100        
851 6         13 add_method($self->associated_class, "add_${accessor}", $attribute->generate('add'), $remove_existing_method);
852 6         16 add_method($self->associated_class, "remove_${accessor}", $attribute->generate('remove'), $remove_existing_method);
853             }
854            
855 58 100       164 if($attribute->associated_class) {
856 14         62 add_method($self->associated_class, "reset_${accessor}", $attribute->generate('reset'), $remove_existing_method);
857 14         34 add_method($self->associated_class, "has_${accessor}", $attribute->generate('has'), $remove_existing_method);
858             }
859             }
860              
861              
862             =item add_method
863              
864             Adds code reference to the class symbol table.
865             Takes a class name, method name and CODE reference.
866              
867             =cut
868              
869             sub add_method {
870 257     257 1 469 my ($class, $name, $code, $remove_existing_method) = @_;
871 257 100       478 remove_method($class, $name) if $remove_existing_method;
872 5     5   26 no strict 'refs';
  5         10  
  5         376  
873 257         258 *{"${class}::$name"} = $code;
  257         1748  
874             }
875              
876              
877             =item remove_method
878              
879             Adds code reference to the class symbol table.
880             Takes a class name, method name and CODE reference.
881              
882             =cut
883              
884             sub remove_method {
885 6     6 1 12 my ($class, $name) = @_;
886 5     5   23 no strict 'refs';
  5         10  
  5         635  
887 6         9 delete ${"${class}::"}{"$name"};
  6         72  
888             }
889              
890              
891              
892             =item constructor_attributes
893              
894             Returns a list of attributes that need be validated and all that have default value
895              
896             =cut
897              
898             sub constructor_attributes {
899 49     49 1 66 my ($self) = @_;
900 49   50     119 my $all_attributes = $self->all_attributes || [];
901 49 100       88 grep {$_->required || defined $_->default} @$all_attributes;
  107         278  
902             }
903              
904             1
905              
906             __END__