File Coverage

blib/lib/Method/Traits.pm
Criterion Covered Total %
statement 62 63 98.4
branch 14 18 77.7
condition 9 10 90.0
subroutine 11 11 100.0
pod 0 1 0.0
total 96 103 93.2


line stmt bran cond sub pod time code
1             package Method::Traits;
2             # ABSTRACT: Apply traits to your methods
3              
4 9     9   610128 use strict;
  9         91  
  9         240  
5 9     9   46 use warnings;
  9         16  
  9         384  
6              
7             our $VERSION = '0.06';
8             our $AUTHORITY = 'cpan:STEVAN';
9              
10 9     9   49 use Carp ();
  9         17  
  9         112  
11 9     9   39 use Scalar::Util ();
  9         17  
  9         130  
12 9     9   1449 use MOP (); # this is how we do most of our work
  9         200493  
  9         179  
13 9     9   61 use Module::Runtime (); # trait provider loading
  9         17  
  9         141  
14              
15             ## ...
16              
17 9     9   2446 use Method::Traits::Meta::Provider;
  9         21  
  9         4470  
18              
19             ## --------------------------------------------------------
20             ## Importers
21             ## --------------------------------------------------------
22              
23             sub import {
24 23     23   28822 my $class = shift;
25              
26 23 100       123 return unless @_;
27              
28 17         43 my @args = @_;
29 17 100 100     98 if ( scalar(@args) == 1 && $args[0] eq ':for_providers' ) {
30             # expand this to make it easier for providers
31 5         9 $args[0] = 'Method::Traits::Meta::Provider';
32             }
33              
34 17         55 $class->import_into( scalar caller, @args );
35             }
36              
37             ## --------------------------------------------------------
38             ## Trait collection
39             ## --------------------------------------------------------
40              
41             our %PROVIDERS_BY_PKG;
42              
43             sub import_into {
44 17     17 0 37 my (undef, $package, @providers) = @_;
45              
46             # add in the providers, so we can
47             # get to them when needed ...
48 17         66 Module::Runtime::use_package_optimistically( $_ ) foreach @providers;
49 17   100     1719 push @{ $PROVIDERS_BY_PKG{ $package } ||=[] } => @providers;
  17         91  
50              
51 17 50       138 my $meta = Scalar::Util::blessed( $package ) ? $package : MOP::Class->new( $package );
52              
53             # no need to install the collectors
54             # if they have already been installed
55             # as they are not different
56             return
57 17 100 66     1154 if $meta->has_method_alias('FETCH_CODE_ATTRIBUTES')
58             && $meta->has_method_alias('MODIFY_CODE_ATTRIBUTES');
59              
60             # now install the collectors ...
61              
62 16         411 my %accepted; # shared state between these two methods ...
63              
64             $meta->alias_method(
65             FETCH_CODE_ATTRIBUTES => sub {
66 48     48   22577 my (undef, $code) = @_;
67             # return just the strings, as expected by attributes ...
68 48 100       142 return $accepted{ $code } ? @{ $accepted{ $code } } : ();
  45         436  
69             }
70 16         84 );
71             $meta->alias_method(
72             MODIFY_CODE_ATTRIBUTES => sub {
73 42     42   6716 my ($pkg, $code, @attrs) = @_;
74              
75 42         60 my @providers = @{ $PROVIDERS_BY_PKG{ $pkg } }; # fetch complete set
  42         92  
76 42         155 my @attributes = map MOP::Method::Attribute->new( $_ ), @attrs;
77              
78 42         1794 my ( %attr_to_handler_map, @unhandled );
79 42         77 foreach my $attribute ( @attributes ) {
80 46         100 my $name = $attribute->name;
81 46   100     354 my $h; $h = $_->can( $name ) and last foreach @providers;
  46         302  
82 46 50       98 if ( $h ) {
83 46         98 $attr_to_handler_map{ $name } = $h;
84             }
85             else {
86 0         0 push @unhandled => $attribute->original;
87             }
88             }
89              
90             # return the bad traits as strings, as expected by attributes ...
91 42 50       91 return @unhandled if @unhandled;
92              
93 42         97 my $klass = MOP::Class->new( $pkg );
94 42         2041 my $method = MOP::Method->new( $code );
95              
96 42         1575 foreach my $attribute ( @attributes ) {
97 46         328 my ($name, $args) = ($attribute->name, $attribute->args);
98 46         1410 my $h = $attr_to_handler_map{ $name };
99              
100 46         190 $h->( $klass, $method, @$args );
101              
102 46 100       6611 if ( MOP::Method->new( $h )->has_code_attributes('OverwritesMethod') ) {
103 26         67 $method = $klass->get_method( $method->name );
104 26 50       3286 Carp::croak('Failed to find new overwriten method ('.$method->name.') in class ('.$meta->name.')')
105             unless defined $method;
106             }
107             }
108              
109             # store the traits we applied ...
110 42         1390 $accepted{ $method->body } = [ map $_->original, @attributes ];
111              
112 42         410 return;
113             }
114 16         625 );
115             }
116              
117             1;
118              
119             __END__