File Coverage

blib/lib/Mite/Trait/HasRoles.pm
Criterion Covered Total %
statement 88 99 88.8
branch 15 26 57.6
condition 6 11 54.5
subroutine 16 17 94.1
pod 0 5 0.0
total 125 158 79.1


line stmt bran cond sub pod time code
1 109     109   2212 use 5.010001;
  109         472  
2 109     109   650 use strict;
  109         340  
  109         3068  
3 109     109   663 use warnings;
  109         250  
  109         5409  
4              
5             use Mite::Miteception -role, -all;
6 109     109   777  
  109         285  
  109         944  
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.011000';
9              
10             requires qw(
11             source
12             native_methods
13             );
14              
15             has roles =>
16             is => rw,
17             isa => ArrayRef[MiteRole],
18             builder => sub { [] };
19 152     152   527  
20             has role_args =>
21             is => rw,
22             isa => Map[ NonEmptyStr, HashRef|Undef ],
23             builder => sub { {} };
24 154     154   515  
25             my $self = shift;
26              
27 135     135 0 302 my %methods;
28             for my $role ( @{ $self->roles } ) {
29 135         319 my $role_args = $self->role_args->{ $role->name } || {};
30 135         326 my %exported = %{ $role->methods_to_export( $role_args ) };
  135         487  
31 13   100     53 for my $name ( sort keys %exported ) {
32 13         28 if ( defined $methods{$name} and $methods{$name} ne $exported{$name} ) {
  13         66  
33 13         70 croak "Conflict between %s and %s; %s must implement %s\n",
34 13 50 33     41 $methods{$name}, $exported{$name}, $self->name, $name;
35             }
36 0         0 else {
37             $methods{$name} = $exported{$name};
38             }
39 13         33 }
40             }
41              
42             # This package provides a native version of these
43             # methods, so don't import.
44             my %native = %{ $self->native_methods };
45             for my $name ( keys %native ) {
46 135         309 delete $methods{$name};
  135         629  
47 135         482 }
48 57         125  
49             # Never propagate
50             delete $methods{$_} for qw(
51             new
52 135         676 DESTROY
53             DOES
54             does
55             __META__
56             __FINALIZE_APPLICATION__
57             CREATE_CLASS
58             APPLY_TO
59             );
60              
61             return \%methods;
62             }
63 135         488  
64             my ( $self, $role ) = @_;
65              
66             my @attr = sort { $a->_order <=> $b->_order }
67 11     11 0 40 values %{ $role->attributes };
68             for my $attr ( @attr ) {
69 5         18 $self->add_attribute( $attr )
70 11         21 unless $self->attributes->{ $attr->name };
  11         79  
71 11         45 }
72             push @{ $self->roles }, $role;
73 10 100       47  
74             return;
75 11         33 }
  11         64  
76              
77 11         33 my ( $self, @names ) = @_;
78              
79             for my $name ( @names ) {
80             my $role = $self->_get_role( $name );
81 11     11 0 47 $self->add_role( $role );
82             }
83 11         40  
84 11         54 return;
85 11         61 }
86              
87             my ( $self, $role_name ) = ( shift, @_ );
88 11         81  
89             my $project = $self->project;
90              
91             # See if it's already loaded
92 11     11   37 my $role = $project->class($role_name);
93             return $role if $role;
94 11         61  
95             # If not, try to load it
96             eval "require $role_name; 1"
97 11         83 or do {
98 11 100       52 my $file_name = $role_name;
99             if ( my $yuck = $project->_module_fakeout_namespace ) {
100             $file_name =~ s/$yuck\:://g;
101             }
102 2 50       128 $file_name =~ s/::/\//g;
103 0         0 $file_name = "lib/$file_name.pm";
104 0 0       0 $project->_load_file( $file_name );
105 0         0 };
106             if ( $INC{'Role/Tiny.pm'} and 'Role::Tiny'->is_role( $role_name ) ) {
107 0         0 require Mite::Role::Tiny;
108 0         0 $role = 'Mite::Role::Tiny'->inhale( $role_name );
109 0         0 }
110             else {
111 2 50 33     18 $role = $project->class( $role_name, 'Mite::Role' );
112 2         820 }
113 2         19 return $role if $role;
114              
115             croak <<"ERROR", $role_name;
116 0         0 %s loaded but is not a recognized role. Mite roles and Role::Tiny
117             roles are the only supported roles. Sorry.
118 2 50       15 ERROR
119             }
120 0         0  
121             my $self = shift;
122             return (
123             $self->name,
124             map( $_->does_list, @{ $self->roles } ),
125             );
126             }
127 24     24 0 57  
128             my $self = shift;
129              
130 24         64 while ( @_ ) {
  24         78  
131             my $role = shift;
132             my $args = Str->check( $_[0] ) ? undef : shift;
133             $self->role_args->{$role} = $args;
134             $self->add_roles_by_name( $role );
135 11     11 0 27 }
136              
137 11         43 return;
138 11         31 }
139 11 50       53  
140 11         252 before inject_mite_functions => sub {
141 11         66 my ( $self, $file, $arg ) = ( shift, @_ );
142              
143             my $requested = sub { $arg->{$_[0]} ? 1 : $arg->{'!'.$_[0]} ? 0 : $arg->{'-all'} ? 1 : $_[1]; };
144 11         34 my $defaults = ! $arg->{'!-defaults'};
145             my $shim = $self->shim_name;
146             my $package = $self->name;
147             my $fake_ns = $self->project->can('_module_fakeout_namespace') && $self->project->_module_fakeout_namespace;
148              
149             no strict 'refs';
150              
151             if ( $requested->( 'with', $defaults ) ) {
152              
153             *{ $package .'::with' } = sub {
154             return $self->handle_with_keyword(
155             defined( $fake_ns )
156 109     109   1023 ? ( map Str->check($_) ? "$fake_ns\::$_" : $_, @_ )
  109         293  
  109         81897  
157             : @_
158             );
159             };
160              
161 0 0   0     $self->imported_keywords->{with} = 'sub { $SHIM->HANDLE_with( $CALLER, @_ ) }';
    0          
162             }
163             };
164              
165             around compilation_stages => sub {
166             my ( $next, $self ) = ( shift, shift );
167             my @stages = $self->$next( @_ );
168             push @stages, qw(
169             _compile_with
170             _compile_does
171             _compile_composed_methods
172             );
173             return @stages;
174             };
175              
176             my $self = shift;
177              
178             my $roles = [ map $_->name, @{ $self->roles } ];
179             return unless @$roles;
180              
181             my $source = $self->source;
182              
183             my $require_list = join "\n\t",
184 123     123   344 map { "require $_;" }
185             # Don't require a role from the same source
186 123         268 grep { !$source || !$source->has_class($_) }
  123         599  
187 123 100       783 @$roles;
188              
189 11         59 my $version_tests = join "\n\t",
190             map { sprintf '%s->VERSION( %s );',
191             B::perlstring( $_ ),
192 2         10 B::perlstring( $self->role_args->{$_}{'-version'} )
193             }
194 11   66     42 grep {
  11         85  
195             $self->role_args->{$_}
196             and $self->role_args->{$_}{'-version'}
197             }
198             @$roles;
199              
200 0         0 my $does_hash = join ", ", map sprintf( "%s => 1", B::perlstring($_) ), $self->does_list;
201              
202             return <<"END";
203 11         37 BEGIN {
204 11 100       43 $require_list
205             $version_tests
206             our \%DOES = ( $does_hash );
207             }
208 11         64 END
209             }
210 11         110  
211             my $self = shift;
212             return <<'CODE'
213             # See UNIVERSAL
214             sub DOES {
215             my ( $self, $role ) = @_;
216             our %DOES;
217             return $DOES{$role} if exists $DOES{$role};
218             return 1 if $role eq __PACKAGE__;
219             if ( $INC{'Moose/Util.pm'} and my $meta = Moose::Util::find_meta( ref $self or $self ) ) {
220 123     123   330 $meta->can( 'does_role' ) and $meta->does_role( $role ) and return 1;
221             }
222             return $self->SUPER::DOES( $role );
223             }
224              
225             # Alias for Moose/Moo-compatibility
226             sub does {
227             shift->DOES( @_ );
228             }
229             CODE
230             }
231              
232             my $self = shift;
233             my $code = '';
234              
235             my %methods = %{ $self->methods_to_import_from_roles };
236             keys %methods or return;
237              
238             $code .= "# Methods from roles\n";
239 123         584 for my $name ( sort keys %methods ) {
240             # Use goto to help namespace::autoclean recognize these as
241             # not being imported methods.
242 123     123   257 $code .= sprintf 'sub %s { goto \&%s; }' . "\n", $name, $methods{$name};
243 123         304 }
244              
245 123         257 return $code;
  123         601  
246 123 100       964 }
247              
248 4         14 around _compile_mop_postamble => sub {
249 4         16 my ( $next, $self ) = ( shift, shift );
250             my $code = $self->$next( @_ );
251              
252 7         32 my @roles = @{ $self->roles || [] }
253             or return $code;
254              
255 4         31 for my $role ( @roles ) {
256             $code .= sprintf "Moose::Util::find_meta( %s )->add_role( Moose::Util::find_meta( %s ) );\n",
257             B::perlstring( $self->name ), B::perlstring( $role->name );
258             }
259              
260             return $code;
261             };
262              
263             1;