File Coverage

blib/lib/MooseX/Method/Signatures/Meta/Method.pm
Criterion Covered Total %
statement 212 214 99.0
branch 62 66 93.9
condition 5 11 45.4
subroutine 48 88 54.5
pod 0 2 0.0
total 327 381 85.8


line stmt bran cond sub pod time code
1             package MooseX::Method::Signatures::Meta::Method;
2             # ABSTRACT: (DEPRECATED) Provides the metaclass for methods with signatures
3              
4             our $VERSION = '0.49';
5              
6 29     39   25614 use Moose;
  29         522608  
  29         284  
7 29     29   188066 use Carp qw/cluck/;
  29         59  
  29         1501  
8 29     29   21953 use Context::Preserve;
  29         13158  
  29         1668  
9 29     29   23890 use Parse::Method::Signatures 1.003014;
  29         10346131  
  29         1643  
10 29     29   29852 use Parse::Method::Signatures::TypeConstraint;
  29         1657313  
  29         1452  
11 29     29   278 use Scalar::Util qw/weaken/;
  29         65  
  29         2089  
12 29     29   147 use Moose::Util qw/does_role/;
  29         64  
  29         307  
13 29     29   8476 use Moose::Util::TypeConstraints;
  29         58  
  29         308  
14 29     29   89440 use MooseX::Meta::TypeConstraint::ForceCoercion;
  29         440638  
  29         1188  
15 29     29   262 use MooseX::Types::Util qw/has_available_type_export/;
  29         61  
  29         1980  
16 29     29   33176 use MooseX::Types::Structured qw/Tuple Dict Optional slurpy/;
  29         11656512  
  29         246  
17 29     29   15632 use MooseX::Types::Moose qw/ArrayRef Str Maybe Object Any CodeRef Bool/;
  29         67  
  29         345  
18 29     29   194631 use MooseX::Method::Signatures::Types qw/Injections Params/;
  29         111  
  29         225  
19 29     29   43860 use aliased 'Parse::Method::Signatures::Param::Named';
  29         975  
  29         271  
20 29     29   375521 use aliased 'Parse::Method::Signatures::Param::Placeholder';
  29         675  
  29         182  
21              
22 29     29   198581 use namespace::autoclean;
  29         63  
  29         249  
23              
24             extends 'Moose::Meta::Method';
25              
26             has signature => (
27             is => 'ro',
28             isa => Str,
29             default => '(@)',
30             required => 1,
31             );
32              
33             has parsed_signature => (
34             is => 'ro',
35             isa => class_type('Parse::Method::Signatures::Sig'),
36             lazy => 1,
37             builder => '_build_parsed_signature',
38             );
39              
40             sub _parsed_signature {
41 0     0   0 cluck '->_parsed_signature is deprecated. use ->parsed_signature instead.';
42 0         0 shift->parsed_signature;
43             }
44              
45             has _lexicals => (
46             is => 'ro',
47             isa => ArrayRef[Str],
48             lazy => 1,
49             builder => '_build__lexicals',
50             );
51              
52             has injectable_code => (
53             is => 'ro',
54             isa => Str,
55             lazy => 1,
56             builder => '_build_injectable_code',
57             );
58              
59             has _positional_args => (
60             is => 'ro',
61             isa => ArrayRef,
62             lazy => 1,
63             builder => '_build__positional_args',
64             );
65              
66             has _named_args => (
67             is => 'ro',
68             isa => ArrayRef,
69             lazy => 1,
70             builder => '_build__named_args',
71             );
72              
73             has _has_slurpy_positional => (
74             is => 'rw',
75             isa => Bool,
76             );
77              
78             has type_constraint => (
79             is => 'ro',
80             isa => class_type('Moose::Meta::TypeConstraint'),
81             lazy => 1,
82             builder => '_build_type_constraint',
83             );
84              
85             has return_signature => (
86             is => 'ro',
87             isa => Str,
88             predicate => 'has_return_signature',
89             );
90              
91             has _return_type_constraint => (
92             is => 'ro',
93             isa => class_type('Moose::Meta::TypeConstraint'),
94             lazy => 1,
95             builder => '_build__return_type_constraint',
96             );
97              
98             has actual_body => (
99             is => 'ro',
100             isa => CodeRef,
101             predicate => '_has_actual_body',
102             );
103              
104             has prototype_injections => (
105             is => 'rw',
106             isa => Injections,
107             trigger => \&_parse_prototype_injections
108             );
109              
110             has _parsed_prototype_injections => (
111             is => 'ro',
112             isa => Params,
113             predicate => '_has_parsed_prototype_injections',
114             writer => '_set_parsed_prototype_injections',
115             );
116              
117             before actual_body => sub {
118             my ($self) = @_;
119             confess "method doesn't have an actual body yet"
120             unless $self->_has_actual_body;
121             };
122              
123             around name => sub {
124             my ($next, $self) = @_;
125             my $ret = $self->$next;
126             confess "method doesn't have a name yet"
127             unless defined $ret;
128             return $ret;
129             };
130              
131             sub _wrapped_body {
132 313     313   1392 my ($class, $self, %args) = @_;
133              
134 313 100       1022 if (exists $args{return_signature}) {
135             return sub {
136 3     3   1255 my @args = ${ $self }->validate(\@_);
  3         12  
137 2         28 return preserve_context { ${ $self }->actual_body->(@args) }
  2         12  
138             after => sub {
139 2 100       17 if (defined (my $msg = ${ $self }->_return_type_constraint->validate(\@_))) {
  2         82  
140 1         3138 confess $msg;
141             }
142 2         21 };
143 4         34 };
144             }
145              
146 309         503 my $actual_body;
147             return sub {
148 133     133   133847 @_ = ${ $self }->validate(\@_);
  133     115   578  
        101      
        90      
        74      
        60      
        91      
        32      
        32      
        32      
        30      
        32      
        30      
        32      
        32      
        30      
        32      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
        0      
149 88   66     433 $actual_body ||= ${ $self }->actual_body;
  67         432  
150 88         152 goto &{ $actual_body };
  88         457  
151 309         2050 };
152              
153             }
154              
155             around wrap => sub {
156             my $orig = shift;
157             my $self;
158             my ($class, $code, %args);
159             if (ref $_[1]) {
160             ($class, $code, %args) = @_;
161             } else {
162             ($class, %args) = @_;
163             $code = delete $args{body};
164             }
165              
166             my $wrapped = $class->_wrapped_body(\$self, %args);
167             $self = $class->$orig($wrapped, %args, $code ? (actual_body => $code) : ());
168              
169             # Vivify the type constraints so TC lookups happen before namespace::clean
170             # removes them
171             $self->type_constraint;
172             $self->_return_type_constraint if $self->has_return_signature;
173              
174             weaken($self->{associated_metaclass})
175             if $self->{associated_metaclass};
176              
177             return $self;
178             };
179              
180             sub reify {
181 1     1 0 558 my $self = shift;
182 1         6 my %args = @_;
183              
184 1         3 my %other_args = %{$self};
  1         13  
185 1         6 delete $other_args{body};
186 1         3 delete $other_args{actual_body};
187              
188 1   33     8 my $body = delete $args{body} || delete $args{actual_body} || $self->body;
189 1         20 my %final_args = (%other_args, %args);
190              
191 1         13 return $self->meta->name->wrap($body, %final_args);
192             }
193              
194             sub _build_parsed_signature {
195 158     158   282 my ($self) = @_;
196 158         6788 return Parse::Method::Signatures->signature(
197             input => $self->signature,
198             from_namespace => $self->package_name,
199             );
200             }
201              
202             sub _build__return_type_constraint {
203 2     2   5 my ($self) = @_;
204 2 50       111 confess 'no return type constraint'
205             unless $self->has_return_signature;
206              
207 2         82 my $parser = Parse::Method::Signatures->new(
208             input => $self->return_signature,
209             from_namespace => $self->package_name,
210             );
211              
212 2         6004 my $param = $parser->_param_typed({});
213             confess 'failed to parse return value type constraint'
214 2 50       2823 unless exists $param->{type_constraints};
215              
216 2         71 return Tuple[$param->{type_constraints}->tc];
217             }
218              
219             sub _param_to_spec {
220 210     210   7394 my ($self, $param) = @_;
221              
222 210         796 my $tc = Any;
223             {
224             # Ensure errors get reported from the right place
225 210         24616 local $Carp::Internal{'MooseX::Method::Signatures::Meta::Method'} = 1;
  210         827  
226 210         633 local $Carp::Internal{'Moose::Meta::Method::Delegation'} = 1;
227 210         569 local $Carp::Internal{'Moose::Meta::Method::Accessor'} = 1;
228 210         532 local $Carp::Internal{'MooseX::Method::Signatures'} = 1;
229 210         576 local $Carp::Internal{'Devel::Declare'} = 1;
230 210 100       9791 $tc = $param->meta_type_constraint
231             if $param->has_type_constraints;
232             }
233              
234 210 100       90717 if ($param->has_constraints) {
235 31         1354 my $cb = join ' && ', map { "sub {${_}}->(\\\@_)" } $param->constraints;
  34         416  
236 31         4364 my $code = eval "sub {${cb}}";
237 31         287 $tc = subtype({ as => $tc, where => $code });
238             }
239              
240 210         32495 my %spec;
241 210 100       6701 if ($param->sigil ne '$') {
242 23         199 $spec{slurpy} = 1;
243 23         228 $tc = slurpy ArrayRef[$tc];
244             }
245              
246 209 100       95797 $spec{tc} = $param->required
247             ? $tc
248             : Optional[$tc];
249              
250 209 100       281252 $spec{default} = $param->default_value
251             if $param->has_default_value;
252              
253 209 100       10425 if ($param->has_traits) {
254 34         246 for my $trait (@{ $param->param_traits }) {
  34         1245  
255 34 50       346 next unless $trait->[1] eq 'coerce';
256 34         115 $spec{coerce} = 1;
257             }
258             }
259              
260 209         1474 return \%spec;
261             }
262              
263             sub _parse_prototype_injections {
264 6     6   49224 my $self = shift;
265              
266 6         17 my @params;
267 6         14 for my $inject (@{ $self->prototype_injections }) {
  6         340  
268 6         13 my $param;
269 6         17 eval {
270 6         56 $param = Parse::Method::Signatures->param($inject);
271             };
272              
273 6 50 33     450134 confess "There was a problem parsing the prototype injection '$inject': $@"
274             if $@ || !defined $param;
275              
276 6         33 push @params, $param;
277             }
278              
279 6         25 my @return = reverse @params;
280 6         508 $self->_set_parsed_prototype_injections(\@return);
281             }
282              
283             sub _build__lexicals {
284 152     152   307 my ($self) = @_;
285 152         6382 my ($sig) = $self->parsed_signature;
286              
287 152         269 my @lexicals;
288              
289 152 100       7985 if ($self->_has_parsed_prototype_injections) {
290             push @lexicals, $_->variable_name
291 3         8 for @{ $self->_parsed_prototype_injections };
  3         172  
292             }
293              
294 152 100       5852 push @lexicals, $sig->has_invocant
295             ? $sig->invocant->variable_name
296             : '$self';
297              
298             push @lexicals,
299             (does_role($_, Placeholder)
300             ? 'undef'
301             : $_->variable_name)
302 152 100       2898 for (($sig->has_positional_params ? $sig->positional_params : ()),
    100          
    100          
303             ($sig->has_named_params ? $sig->named_params : ()));
304              
305 152         142478 return \@lexicals;
306             }
307              
308             sub _build_injectable_code {
309 152     152   340 my ($self) = @_;
310 152         283 my $vars = join q{,}, @{ $self->_lexicals };
  152         6368  
311 152         6684 return "my (${vars}) = \@_;";
312             }
313              
314             sub _build__positional_args {
315 158     158   303 my ($self) = @_;
316 158         6686 my $sig = $self->parsed_signature;
317              
318 157         407 my @positional;
319 157 100       8822 if ($self->_has_parsed_prototype_injections) {
320             push @positional, map {
321 3         17 $self->_param_to_spec($_)
322 3         8 } @{ $self->_parsed_prototype_injections };
  3         171  
323             }
324              
325 157 100       6335 push @positional, $sig->has_invocant
326             ? $self->_param_to_spec($sig->invocant)
327             : { tc => Object };
328              
329 157         18560 my $slurpy = 0;
330 157 100       1006 if ($sig->has_positional_params) {
331 101         17248 for my $param ($sig->positional_params) {
332 126         9103 my $spec = $self->_param_to_spec($param);
333 125 100 50     716 $slurpy ||= 1 if $spec->{slurpy};
334 125         359 push @positional, $spec;
335             }
336             }
337              
338 156         11200 $self->_has_slurpy_positional($slurpy);
339 156         6547 return \@positional;
340             }
341              
342             sub _build__named_args {
343 156     156   313 my ($self) = @_;
344 156         6530 my $sig = $self->parsed_signature;
345              
346             # triggering building of positionals before named params is important
347             # because the latter needs to know if there have been any slurpy
348             # positionals to report errors
349 156         6466 $self->_positional_args;
350              
351 156         267 my @named;
352              
353 156 100       824 if ($sig->has_named_params) {
354 52 100       10507 confess 'Named parameters cannot be combined with slurpy positionals'
355             if $self->_has_slurpy_positional;
356 51         336 for my $param ($sig->named_params) {
357 61         6287 push @named, $param->label => $self->_param_to_spec($param);
358             }
359             }
360              
361 155         12632 return \@named;
362             }
363              
364             sub _build_type_constraint {
365 158     158   277 my ($self) = @_;
366 158         382 my ($positional, $named) = map { $self->$_ } map { "_${_}_args" } qw/positional named/;
  314         13592  
  316         990  
367              
368             my $tc = Tuple[
369 281         815 Tuple[ map { $_->{tc} } @{ $positional } ],
  155         333  
370 155 100       496 Dict[ map { ref $_ ? $_->{tc} : $_ } @{ $named } ],
  122         648  
  155         783  
371             ];
372              
373             my $coerce_param = sub {
374 313     313   576 my ($spec, $value) = @_;
375 313 100       1408 return $value unless exists $spec->{coerce};
376 7         47 return $spec->{tc}->coerce($value);
377 155         1680913 };
378              
379 155         342 my %named = @{ $named };
  155         501  
380              
381             coerce $tc,
382             from ArrayRef,
383             via {
384 136     136   619433 my (@positional_args, %named_args);
385              
386 136         291 my $i = 0;
387 136         216 for my $param (@{ $positional }) {
  136         391  
388 260         636 push @positional_args, map { $coerce_param->($param, $_) }
389 291         1374 $#{ $_ } < $i
390 291 100       408 ? (exists $param->{default} ? eval $param->{default} : ())
    100          
391             : $_->[$i];
392 291         3507 $i++;
393             }
394              
395 136 100       375 if (%named) {
    100          
396 47         97 my @rest = @{ $_ }[$i .. $#{ $_ }];
  47         143  
  47         103  
397 47 100       614 confess "Expected named arguments but didn't find an even-sized list"
398             unless @rest % 2 == 0;
399 44         149 my %rest = @rest;
400              
401 44         194 while (my ($key, $spec) = each %named) {
402 68 100       191 if (exists $rest{$key}) {
403 48         137 $named_args{$key} = $coerce_param->($spec, delete $rest{$key});
404 48         1354 next;
405             }
406              
407 20 100       89 if (exists $spec->{default}) {
408 5         320 $named_args{$key} = $coerce_param->($spec, eval $spec->{default});
409             }
410             }
411              
412 44         146 @named_args{keys %rest} = values %rest;
413             }
414 89         299 elsif ($#{ $_ } >= $i) {
415 8         22 push @positional_args, @{ $_ }[$i .. $#{ $_ }];
  8         32  
  8         23  
416             }
417              
418 133         896 return [\@positional_args, \%named_args];
419 155         733 };
420              
421 155         331376 return MooseX::Meta::TypeConstraint::ForceCoercion->new(
422             type_constraint => $tc,
423             );
424             }
425              
426             sub validate {
427 136     136 0 254 my ($self, $args) = @_;
428              
429 136         242 my @named = grep { !ref $_ } @{ $self->_named_args };
  142         389  
  136         6447  
430              
431 136         225 my $coerced;
432 136 100       5798 if (defined (my $msg = $self->type_constraint->validate($args, \$coerced))) {
433 43         220715 confess $msg;
434             }
435              
436 90         257391 return @{ $coerced->[0] }, map { $coerced->[1]->{$_} } @named;
  90         475  
  37         191  
437             }
438              
439             __PACKAGE__->meta->make_immutable;
440              
441             #pod =pod
442             #pod
443             #pod =for stopwords metaclass
444             #pod
445             #pod =cut
446              
447             1;
448              
449             __END__
450              
451             =pod
452              
453             =encoding UTF-8
454              
455             =head1 NAME
456              
457             MooseX::Method::Signatures::Meta::Method - (DEPRECATED) Provides the metaclass for methods with signatures
458              
459             =head1 VERSION
460              
461             version 0.49
462              
463             =for stopwords metaclass
464              
465             =head1 SUPPORT
466              
467             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Method-Signatures>
468             (or L<bug-MooseX-Method-Signatures@rt.cpan.org|mailto:bug-MooseX-Method-Signatures@rt.cpan.org>).
469              
470             There is also a mailing list available for users of this distribution, at
471             L<http://lists.perl.org/list/moose.html>.
472              
473             There is also an irc channel available for users of this distribution, at
474             irc://irc.perl.org/#moose.
475              
476             I am also usually active on irc, as 'ether' at C<irc.perl.org>.
477              
478             =head1 AUTHOR
479              
480             Florian Ragwitz <rafl@debian.org>
481              
482             =head1 COPYRIGHT AND LICENCE
483              
484             This software is copyright (c) 2008 by Florian Ragwitz.
485              
486             This is free software; you can redistribute it and/or modify it under
487             the same terms as the Perl 5 programming language system itself.
488              
489             =cut