File Coverage

blib/lib/MooseX/Method.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package MooseX::Method;
2              
3 3     3   110788 use Moose;
  0            
  0            
4              
5             use B qw/svref_2object/;
6             use Carp qw/croak/;
7             use Class::MOP;
8             use Moose::Meta::Class;
9             use Moose::Util qw/does_role/;
10             use MooseX::Meta::Method::Signature;
11             use MooseX::Meta::Method::Signature::Compiled;
12             use MooseX::Meta::Signature::Named;
13             use MooseX::Meta::Signature::Named::Compiled;
14             use MooseX::Meta::Signature::Positional;
15             use MooseX::Meta::Signature::Positional::Compiled;
16             use MooseX::Meta::Signature::Combined;
17             use MooseX::Meta::Signature::Combined::Compiled;
18             use MooseX::Method::Exception;
19             use Sub::Exporter;
20             use Sub::Name qw/subname/;
21              
22             our $VERSION = '0.44';
23              
24             our $AUTHORITY = 'cpan:BERLE';
25              
26             my %exports = (
27             method => \&_method_generator,
28             named => \&_named_generator,
29             positional => \&_positional_generator,
30             semi => \&_combined_generator,
31             combined => \&_combined_generator,
32             attr => \&_attr_generator,
33             default_attr => \&_default_attr_generator,
34             );
35              
36             my $exporter = Sub::Exporter::build_exporter({
37             exports => \%exports,
38             groups => {
39             default => [':all' => { compiled => 0 }],
40             compiled => [':all' => { compiled => 1 }],
41             }
42             });
43              
44             sub import {
45             my $class = caller;
46              
47             return if $class eq 'main';
48              
49             Moose::Meta::Class->initialize ($class)
50             unless Class::MOP::does_metaclass_exist ($class);
51              
52             goto $exporter;
53             }
54              
55             sub unimport {
56             my $class = caller;
57              
58             foreach my $name (keys %exports) {
59             if (defined &{$class . '::' . $name}) {
60             my $keyword = \&{$class . '::' . $name};
61              
62             my $pkg_name = eval { svref_2object($keyword)->GV->STASH->NAME };
63              
64             next if $@;
65              
66             next if $pkg_name ne 'MooseX::Method';
67              
68             no strict qw/refs/;
69              
70             delete ${$class . '::'}{$name};
71             }
72             }
73             }
74              
75             sub _positional_generator {
76             my $signature_metaclass;
77              
78             if ($_[2]->{compiled}) {
79             $signature_metaclass = 'MooseX::Meta::Signature::Positional::Compiled';
80             } else {
81             $signature_metaclass = 'MooseX::Meta::Signature::Positional';
82             }
83            
84             return subname 'MooseX::Method::positional' => sub { eval { $signature_metaclass->new (@_) } || croak "$@" };
85             }
86              
87             sub _named_generator {
88             my $signature_metaclass;
89              
90             if ($_[2]->{compiled}) {
91             $signature_metaclass = 'MooseX::Meta::Signature::Named::Compiled';
92             } else {
93             $signature_metaclass = 'MooseX::Meta::Signature::Named';
94             }
95              
96             return subname 'MooseX::Method::named' => sub { eval { $signature_metaclass->new (@_) } || croak "$@" };
97             }
98              
99             sub _combined_generator {
100             my $signature_metaclass;
101              
102             if ($_[2]->{compiled}) {
103             $signature_metaclass = 'MooseX::Meta::Signature::Combined::Compiled';
104             } else {
105             $signature_metaclass = 'MooseX::Meta::Signature::Combined';
106             }
107              
108             return subname 'MooseX::Method::combined' => sub { eval { $signature_metaclass->new (@_) } || croak "$@" };
109             }
110              
111             sub _attr_generator {
112             return subname 'MooseX::Method::attr' => sub { return { @_ } };
113             }
114              
115             sub _default_attr_generator {
116             return subname 'MooseX::Method::default_attr' => sub {
117             my $class = caller;
118              
119             my $meta = Class::MOP::get_metaclass_by_name ($class);
120              
121             $meta->add_method (_default_method_attributes => sub { return { @_ } });
122              
123             return;
124             }
125             }
126              
127             sub _method_generator {
128             my $default_method_metaclass;
129              
130             if ($_[2]->{compiled}) {
131             $default_method_metaclass = 'MooseX::Meta::Method::Signature::Compiled';
132             } else {
133             $default_method_metaclass = 'MooseX::Meta::Method::Signature';
134             }
135              
136             return subname 'MooseX::Method::method' => sub {
137             my $name = shift;
138              
139             croak "You must supply a method name"
140             unless defined $name && ! ref $name;
141              
142             my $class = caller;
143              
144             my ($signature,$coderef,$method,$meta);
145              
146             my $local_attributes = {};
147              
148             if ($class->can ('meta')) {
149             $meta = $class->meta;
150             } else {
151             $meta = Class::MOP::get_metaclass_by_name ($class);
152             }
153              
154             for (@_) {
155             if (does_role ($_,'MooseX::Meta::Signature')) {
156             $signature = $_;
157             } elsif (ref $_ eq 'CODE') {
158             $coderef = $_;
159             } elsif (ref $_ eq 'HASH') {
160             $local_attributes = $_;
161             } else {
162             croak "I have no idea what to do with ($_)";
163             }
164             }
165              
166             unless (defined $coderef) {
167             if ($meta->isa ('Moose::Meta::Role')) {
168             $meta->add_required_methods ($name);
169              
170             return;
171             }
172            
173             croak "You didn't provide a coderef";
174             }
175              
176             my $attributes;
177              
178             # Have a method that allows default attribute settings for methods.
179             if ($class->can ('_default_method_attributes')) {
180             $attributes = $class->_default_method_attributes ($name);
181              
182             croak "_default_method_attributes exists but does not return a hashref"
183             unless ref $attributes eq 'HASH';
184             } else {
185             $attributes = {};
186             }
187              
188             $attributes = { %$attributes,%$local_attributes };
189              
190             my $method_metaclass = $attributes->{metaclass} || $default_method_metaclass;
191              
192             subname "$class\::$name", $coderef;
193              
194             if (defined $signature) {
195             $method = $method_metaclass->wrap_with_signature (
196             $signature,$coderef,$class,$name
197             );
198             } else {
199             $method = $method_metaclass->wrap ($coderef,
200             package_name => $class, name => $name
201             );
202             }
203              
204             # For Devel::Cover
205             $meta->add_package_symbol ("&__real_${name}" => $coderef);
206              
207             $meta->add_method ($name => $method);
208              
209             return $method;
210             }
211             }
212              
213             1;
214              
215             __END__
216              
217             =pod
218              
219             =head1 NAME
220              
221             MooseX::Method - (DEPRECATED) Method declaration with type checking
222              
223             =head1 SYNOPSIS
224              
225             package Foo;
226              
227             use MooseX::Method; # Or use MooseX::Method qw/:compiled/
228              
229             method hello => named (
230             who => { isa => 'Str',required => 1 },
231             age => { isa => 'Int',required => 1 },
232             ) => sub {
233             my ($self,$args) = @_;
234              
235             print "Hello $args->{who}, I am $args->{age} years old!\n";
236             };
237              
238             method morning => positional (
239             { isa => 'Str',required => 1 },
240             ) => sub {
241             my ($self,$name) = @_;
242              
243             print "Good morning $name!\n";
244             };
245              
246             method greet => combined (
247             { isa => 'Str' },
248             excited => { isa => 'Bool',default => 0 },
249             ) => sub {
250             my ($self,$name,$args) = @_;
251              
252             if ($args->{excited}) {
253             print "GREETINGS $name!\n";
254             } else {
255             print "Hi $name!\n";
256             }
257             };
258              
259             no MooseX::Method; # Remove the MooseX::Method keywords.
260              
261             Foo->hello (who => 'world',age => 42); # This works.
262              
263             Foo->morning ('Jens'); # This too.
264              
265             Foo->greet ('Jens',excited => 1); # And this as well.
266              
267             Foo->hello (who => 'world',age => 'fortytwo'); # This doesn't.
268              
269             Foo->morning; # This neither.
270              
271             Foo->greet; # Won't work.
272              
273             =head1 DEPRECATION NOTICE
274              
275             This module has been deprecated in favor of L<MooseX::Method::Signatures>. It
276             is being maintained purely for people who need more time to change their
277             implementations. It should not be used for new code.
278              
279             =head1 DESCRIPTION
280              
281             =head2 The problem
282              
283             This module is an attempt to solve a problem I've often encountered but
284             never really found any good solution for: validation of method
285             parameters. How many times have we all ourselves writing code like this:
286              
287             sub foo {
288             my ($self,$args) = @_;
289              
290             die "Invalid arg1"
291             unless (defined $arg->{bar} && $arg->{bar} =~ m/bar/);
292             }
293              
294             Manual parameter validation is a tedious, repetive process and
295             maintaining it consistently throughout your code can be downright hard
296             sometimes. Modules like L<Params::Validate> makes the job a bit easier,
297             but it doesn't do much for elegance and it still requires more weird
298             code than what should, strictly speaking, be neccesary.
299              
300             =head2 The solution
301              
302             MooseX::Method to the rescue! It lets you declare which parameters
303             people should pass to your method using Moose-style declaration and
304             Moose types. It doesn't get much Moosier than this.
305              
306             =head1 DECLARING METHODS
307              
308             method $name => sub {};
309              
310             method $name => named () => sub {};
311              
312             The exported function C<method> installs a method into the class which
313             call it. The first parameter it takes is the name of the method. The
314             rest of the parameters need not be in any particular order, though it's
315             probably best for the sake of readability to keep the subroutine at the
316             end.
317              
318             There are two different elements you need to be aware of: the
319             signature and the parameter. A signature is (for the purpose of this
320             document) a collection of parameters. A parameter is a collection of
321             requirements that an individual argument needs to satisfy. No matter
322             what kind of signature you use, these properties are declared the
323             same way, although specific properties may behave differently
324             depending on the particular signature type.
325              
326             As of version 0.31, signatures are optional in method declarations. If
327             one is not provided, arguments will be passed directly to the coderef.
328              
329             =head2 Signatures
330              
331             MooseX::Method ships with three different signature types. Once the
332             internal API stabilizes, you'll be able to implement your own signatures
333             easily.
334              
335             The three different signatures types are shown below:
336              
337             named (
338             foo => { isa => 'Int',required => 1 },
339             bar => { isa => 'Int' },
340             )
341              
342             # And methods declared are called like...
343              
344             $foo->mymethod (foo => 1,bar => 2);
345              
346             positional (
347             { isa => 'Int',required => 1 },
348             { isa => 'Int' },
349             )
350              
351             $foo->mymethod (1,2);
352              
353             combined (
354             { isa => 'Int' },
355             foo => { isa => 'Int' },
356             )
357              
358             $foo->mymethod (1,foo => 2);
359              
360             The named signature type will let you specify names for the individual
361             parameters. The example above declares two parameters, foo and bar,
362             where foo is mandatory. Read more about parameter properties below.
363              
364             The positional signature type lets you, surprisingly, declare positional
365             unnamed parameters. If a parameter has the 'required' property set in a
366             positional signature, a parameter is counted as provided if the argument
367             list is equal or larger to its position. One thing about this is that it
368             leads to a situation where a parameter is implicitly required if a later
369             parameter is explicitly required. Even so, you should always mark all
370             required parameters explicitly.
371              
372             The combined signature type combines the two signature types above. You
373             may declare both named and positional parameters. Parameters do not need
374             to come in any particular order (although positional parameters must be
375             ordered correctly relative to each other like with the positional
376             signature) so it's possible to declare a combined signature like this:
377              
378             combined (
379             { isa => 'Int' },
380             foo => { isa => 'Int' },
381             { isa => 'Int' },
382             bar => { isa => 'Int' },
383             )
384              
385             This is however not recommended for the sake of readability. Put
386             positional arguments first, then named arguments last, which is the same
387             order combined signature methods receive them. Also be aware that all
388             positional parameters are always required in a combined signature. Named
389             parameters may be both optional or required however.
390              
391             =head2 Parameters
392              
393             Currently, a parameter may set any of the following fields:
394              
395             =over 4
396              
397             =item B<isa>
398              
399             If a value is provided, it must satisfy the constraints of the type
400             specified in this field. This field should accept the same values
401             as its counterpart in Moose attributes, see the Moose documentation
402             for more details on what you can use.
403              
404             =item B<does>
405              
406             Require that the value provided is able to do a certain role. It's
407             implied that the value must also be blessed, although setting this
408             property does not alter the isa property.
409              
410             =item B<default>
411              
412             Sets the parameter to a default value if the user does not provide it.
413              
414             =item B<required>
415              
416             If this field is set, supplying a value to the method isn't optional
417             but the value may be supplied by the default field.
418              
419             =item B<coerce>
420              
421             If the type supports coercion, attempt to coerce the value provided if
422             it does not satisfy the requirements of isa. See Moose for examples
423             of how to coerce.
424              
425             =item B<metaclass>
426              
427             This is used as parameter metaclass if specified. If you don't know
428             what this means, read the documentation for Moose.
429              
430             =back
431              
432             =head2 Attributes
433              
434             To set a method attribute, use the following syntax:
435              
436             method foo => attr (
437             attribute => $value,
438             ) => sub {};
439              
440             You can set the default method attributes for a class by using the
441             function default_attr like this:
442              
443             default_attr (attribute => $value);
444              
445             method foo => attr (
446             overridden_attribute => $value,
447             ) => sub {};
448              
449             If you discover any attributes other than those listed here while diving
450             through the code, they're not guaranteed to be in the next release.
451              
452             =over 4
453              
454             =item B<metaclass>
455              
456             Sets the metaclass to use when creating the method.
457              
458             =back
459              
460             =head1 EXPORTED FUNCTIONS
461              
462             =over 4
463              
464             =item B<method>
465              
466             The function for declaring methods.
467              
468             =item B<named>
469              
470             A function for constructing a named signature.
471              
472             =item B<positional>
473              
474             A function for constructing a positional signature.
475              
476             =item B<combined>
477              
478             A function for constructing a combined signature.
479              
480             =item B<semi>
481              
482             An alias for the combined structure. B<Will be removed post version 1.0.>
483              
484             =item B<attr>
485              
486             A function for declaring method attributes.
487              
488             =item B<default_attr>
489              
490             A function for setting the default attributes on methods of a class.
491              
492             =back
493              
494             =head1 ROLES
495              
496             Inside Moose roles, MooseX::Method can be used as sugar for declaring
497             a required method. This is done by not attaching a coderef to method
498             declaration, like this...
499              
500             method foo => ();
501              
502             Which will make MooseX::Method add the method to the list of required
503             methods instead of making it a real method in the role. Signatures in
504             such declarations are at the moment not used, but I'm working with
505             stevan on making it possible to require a specific signature.
506              
507             =head1 COMPILATION SUPPORT
508              
509             As of 0.40, MooseX::Method has experimental support for compiling the
510             signatures into Perl code and inlining it to achieve a significant
511             performance improvement. This behaviour is not enabled by default since
512             it is not yet tested extensively, and may or may not be severely
513             bugged -- but if you dare, you can enable inline compilation with
514              
515             use MooseX::Method qw/:compiled/;
516              
517             And all methods within this class will take adventage of the new
518             experimental feature. This does not affect classes that do not
519             explicitly enable it; the effect is local. If you try this and
520             get an error using it, please make a small test case and send it
521             to me.
522              
523             =head1 FUTURE
524              
525             I'm considering using a param() function to declare individual
526             parameters, but I feel this might have too high a risk of clashing with
527             existing functions of other modules. Your thoughts on the subject are
528             welcome.
529              
530             =head1 CAVEATS
531              
532             Methods are added to the class at runtime, which obviously means they
533             won't be available to play with at compile-time. Moose won't mind this
534             but a few other modules probably will. A workaround for this that
535             sometimes works is to encapsulate the method declarations in a BEGIN
536             block.
537              
538             There's also a problem related to how roles are loaded in Moose. Since
539             both MooseX::Method methods and Moose roles are loaded at runtime, any
540             methods a role requires in some way must be declared before the 'with'
541             statement. This affects things like 'before' and 'after'.
542              
543             =head1 ACKNOWLEDGEMENTS
544              
545             =over 4
546              
547             =item Stevan Little for making Moose and luring me into the
548             world of metafoo.
549              
550             =item Max Kanat-Alexander for testing.
551              
552             =item Christopher Nehren for documentation review.
553              
554             =back
555              
556             =head1 SEE ALSO
557              
558             =over 4
559              
560             =item L<Moose>
561              
562             =item The #moose channel on irc.perl.org
563              
564             =back
565              
566             =head1 BUGS
567              
568             Most software has bugs. This module probably isn't an exception.
569             If you find a bug please either email me, or add the bug to cpan-RT.
570              
571             =head1 AUTHOR
572              
573             Anders Nor Berle E<lt>debolaz@gmail.comE<gt>
574              
575             =head1 COPYRIGHT AND LICENSE
576              
577             Copyright 2007 by Anders Nor Berle.
578              
579             This library is free software; you can redistribute it and/or modify
580             it under the same terms as Perl itself.
581              
582             =cut
583