File Coverage

blib/lib/Moxie/Traits/Provider/Constructor.pm
Criterion Covered Total %
statement 67 70 95.7
branch 23 24 95.8
condition 3 3 100.0
subroutine 12 12 100.0
pod 0 1 0.0
total 105 110 95.4


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