File Coverage

blib/lib/Method/Traits.pm
Criterion Covered Total %
statement 62 63 98.4
branch 14 18 77.7
condition 10 12 83.3
subroutine 11 11 100.0
pod 0 1 0.0
total 97 105 92.3


line stmt bran cond sub pod time code
1             package Method::Traits;
2             # ABSTRACT: Apply traits to your methods
3              
4 9     9   584860 use strict;
  9         86  
  9         230  
5 9     9   42 use warnings;
  9         13  
  9         345  
6              
7             our $VERSION = '0.07';
8             our $AUTHORITY = 'cpan:STEVAN';
9              
10 9     9   39 use Carp ();
  9         16  
  9         91  
11 9     9   35 use Scalar::Util ();
  9         14  
  9         112  
12 9     9   1395 use MOP (); # this is how we do most of our work
  9         209172  
  9         160  
13 9     9   51 use Module::Runtime (); # trait provider loading
  9         14  
  9         127  
14              
15             ## ...
16              
17 9     9   2496 use Method::Traits::Meta::Provider;
  9         20  
  9         4063  
18              
19             ## --------------------------------------------------------
20             ## Importers
21             ## --------------------------------------------------------
22              
23             sub import {
24 23     23   30157 my $class = shift;
25              
26 23 100       124 return unless @_;
27              
28 17         43 my @args = @_;
29 17 100 100     104 if ( scalar(@args) == 1 && $args[0] eq ':for_providers' ) {
30             # expand this to make it easier for providers
31 5         8 $args[0] = 'Method::Traits::Meta::Provider';
32             }
33              
34 17         59 $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 42 my (undef, $package, @providers) = @_;
45              
46             # add in the providers, so we can
47             # get to them when needed ...
48 17         67 Module::Runtime::use_package_optimistically( $_ ) foreach @providers;
49 17   100     1608 push @{ $PROVIDERS_BY_PKG{ $package } ||=[] } => @providers;
  17         93  
50              
51 17 50       134 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     1039 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         406 my %accepted; # shared state between these two methods ...
63              
64             $meta->alias_method(
65             FETCH_CODE_ATTRIBUTES => sub {
66 48     48   28376 my (undef, $code) = @_;
67             # return just the strings, as expected by attributes ...
68 48 100       170 return $accepted{ $code } ? @{ $accepted{ $code } } : ();
  45         534  
69             }
70 16         86 );
71             $meta->alias_method(
72             MODIFY_CODE_ATTRIBUTES => sub {
73 42     42   6965 my ($pkg, $code, @attrs) = @_;
74              
75 42   50     66 my @providers = @{ $PROVIDERS_BY_PKG{ $pkg } ||=[] }; # fetch complete set
  42         146  
76 42         157 my @attributes = map MOP::Method::Attribute->new( $_ ), @attrs;
77              
78 42         1788 my ( %attr_to_handler_map, @unhandled );
79 42         77 foreach my $attribute ( @attributes ) {
80 46         97 my $name = $attribute->name;
81 46   100     641 my $h; $h = $_->can( $name ) and last foreach @providers;
  46         310  
82 46 50       105 if ( $h ) {
83 46         100 $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       82 return @unhandled if @unhandled;
92              
93 42         96 my $klass = MOP::Class->new( $pkg );
94 42         2038 my $method = MOP::Method->new( $code );
95              
96 42         1628 foreach my $attribute ( @attributes ) {
97 46         329 my ($name, $args) = ($attribute->name, $attribute->args);
98 46         1471 my $h = $attr_to_handler_map{ $name };
99              
100 46         170 $h->( $klass, $method, @$args );
101              
102 46 100       7028 if ( MOP::Method->new( $h )->has_code_attributes('OverwritesMethod') ) {
103 26         78 $method = $klass->get_method( $method->name );
104 26 50       3658 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         1358 $accepted{ $method->body } = [ map $_->original, @attributes ];
111              
112 42         412 return;
113             }
114 16         613 );
115             }
116              
117             1;
118              
119             __END__