File Coverage

blib/lib/decorators/providers/constructor.pm
Criterion Covered Total %
statement 52 55 94.5
branch 19 24 79.1
condition 3 3 100.0
subroutine 13 13 100.0
pod 1 1 100.0
total 88 96 91.6


line stmt bran cond sub pod time code
1             package decorators::providers::constructor;
2             # ABSTRACT: A set of decorators to generate BUILDARG methods
3              
4 1     1   497 use strict;
  1         2  
  1         23  
5 1     1   4 use warnings;
  1         1  
  1         21  
6              
7 1     1   4 use decorators ':for_providers';
  1         2  
  1         7  
8              
9 1     1   34 use Carp ();
  1         2  
  1         13  
10 1     1   4 use MOP::Util ();
  1         12  
  1         568  
11              
12             our $VERSION = '0.01';
13             our $AUTHORITY = 'cpan:STEVAN';
14              
15             sub strict : Decorator : CreateMethod {
16 4     4 1 217 my ( $meta, $method, %signature ) = @_;
17              
18             # XXX:
19             # Consider perhaps supporting something
20             # like the Perl 6 signature format here,
21             # which would give us a more sophisticated
22             # way to specify the constructor API
23             #
24             # The way MAIN is handled is good inspiration maybe ...
25             # http://perl6maven.com/parsing-command-line-arguments-perl6
26             #
27             # - SL
28              
29 4         11 my $class_name = $meta->name;
30 4         49 my $method_name = $method->name;
31              
32 4 50       48 Carp::confess('The `strict` trait can only be applied to BUILDARGS')
33             if $method_name ne 'BUILDARGS';
34              
35 4 100       10 if ( %signature ) {
36              
37 3         14 my @all = sort keys %signature;
38 3         14 my @required = grep !/\?$/, @all;
39              
40 3         6 my $max_arity = 2 * scalar @all;
41 3         5 my $min_arity = 2 * scalar @required;
42              
43             # use Data::Dumper;
44             # warn Dumper {
45             # class => $meta->name,
46             # all => \@all,
47             # required => \@required,
48             # min_arity => $min_arity,
49             # max_arity => $max_arity,
50             # };
51              
52             $meta->add_method('BUILDARGS' => sub {
53 18     18   23054 my ($self, @args) = @_;
        18      
        18      
        18      
54              
55 18         33 my $arity = scalar @args;
56              
57 18 100 100     1896 Carp::confess('Constructor for ('.$class_name.') expected '
    100          
58             . (($max_arity == $min_arity)
59             ? ($min_arity)
60             : ('between '.$min_arity.' and '.$max_arity))
61             . ' arguments, got ('.$arity.')')
62             if $arity < $min_arity || $arity > $max_arity;
63              
64 10         25 my $proto = $self->UNIVERSAL::Object::BUILDARGS( @args );
65              
66 10         81 my @missing;
67             # make sure all the expected parameters exist ...
68 10         29 foreach my $param ( @required ) {
69 13 100       29 push @missing => $param unless exists $proto->{ $param };
70             }
71              
72 10 100       976 Carp::confess('Constructor for ('.$class_name.') missing (`'.(join '`, `' => @missing).'`) parameters, got (`'.(join '`, `' => sort keys %$proto).'`), expected (`'.(join '`, `' => @all).'`)')
73             if @missing;
74              
75 5         8 my (%final, %super);
76              
77             #warn "---------------------------------------";
78             #warn join ', ' => @all;
79              
80             # do any kind of slot assignment shuffling needed ....
81 5         10 foreach my $param ( @all ) {
82              
83             #warn "CHECKING param: $param";
84              
85 9         11 my $from = $param;
86 9         24 $from =~ s/\?$//;
87 9         15 my $to = $signature{ $param };
88              
89             #warn "PARAM: $param FROM: ($from) TO: ($to)";
90              
91 9 50       15 if ( $to =~ /^super\((.*)\)$/ ) {
92             $super{ $1 } = delete $proto->{ $from }
93 0 0       0 if $proto->{ $from };
94             }
95             else {
96 9 100       18 if ( exists $proto->{ $from } ) {
97              
98             #use Data::Dumper;
99             #warn "BEFORE:", Dumper $proto;
100              
101             # now grab the slot by the correct name ...
102 7         31 $final{ $to } = delete $proto->{ $from };
103              
104             #warn "AFTER:", Dumper $proto;
105             }
106             #else {
107             #use Data::Dumper;
108             #warn "NOT FOUND ($from) :", Dumper $proto;
109             #}
110             }
111             }
112              
113             # inherit keys ...
114 5 50       13 if ( keys %super ) {
115 0         0 my $super_proto = $self->next::method( %super );
116 0         0 %final = ( %$super_proto, %final );
117             }
118              
119 5 100       14 if ( keys %$proto ) {
120              
121             #use Data::Dumper;
122             #warn Dumper +{
123             # proto => $proto,
124             # final => \%final,
125             # super => \%super,
126             # meta => {
127             # class => $meta->name,
128             # all => \@all,
129             # required => \@required,
130             # min_arity => $min_arity,
131             # max_arity => $max_arity,
132             # }
133             #};
134              
135 1         182 Carp::confess('Constructor for ('.$class_name.') got unrecognized parameters (`'.(join '`, `' => keys %$proto).'`)');
136             }
137              
138 4         12 return \%final;
139 3         26 });
140             }
141             else {
142             $meta->add_method('BUILDARGS' => sub {
143 5     5   5129 my ($self, @args) = @_;
        5      
144 5 100       1001 Carp::confess('Constructor for ('.$class_name.') expected 0 arguments, got ('.(scalar @args).')')
145             if @args;
146 1         5 return $self->UNIVERSAL::Object::BUILDARGS();
147 1         7 });
148             }
149 1     1   7 }
  1         2  
  1         8  
150              
151             1;
152              
153             __END__