File Coverage

blib/lib/Mite/Role.pm
Criterion Covered Total %
statement 64 70 91.4
branch 11 14 78.5
condition 1 2 50.0
subroutine 12 16 75.0
pod 0 3 0.0
total 88 105 83.8


line stmt bran cond sub pod time code
1 15     15   316 use 5.010001;
  15         65  
2 15     15   95 use strict;
  15         34  
  15         340  
3 15     15   81 use warnings;
  15         34  
  15         740  
4              
5             package Mite::Role;
6 15     15   118 use Mite::Miteception -all;
  15         60  
  15         119  
7             extends qw(
8             Mite::Package
9             );
10             with qw(
11             Mite::Trait::HasRequiredMethods
12             Mite::Trait::HasAttributes
13             Mite::Trait::HasRoles
14             Mite::Trait::HasMethods
15             Mite::Trait::HasMOP
16             );
17              
18             our $AUTHORITY = 'cpan:TOBYINK';
19             our $VERSION = '0.012000';
20              
21 15     15   147 use Path::Tiny;
  15         40  
  15         847  
22 15     15   94 use B ();
  15         68  
  15         15252  
23              
24 20     20 0 55 sub kind { 'role' }
25              
26             sub methods_to_export {
27 12     12 0 50 my ( $self, $role_args ) = @_;
28              
29 12         22 my %methods = %{ $self->methods_to_import_from_roles };
  12         39  
30 12         44 my %native = %{ $self->native_methods };
  12         41  
31 12         59 my $package = $self->name;
32              
33 12         38 for my $name ( keys %native ) {
34 10         32 $methods{$name} = "$package\::$name";
35             }
36              
37 12 100       59 if ( my $excludes = $role_args->{'-excludes'} ) {
38 1 50       6 for my $excluded ( ref( $excludes ) ? @$excludes : $excludes ) {
39 1         3 delete $methods{$excluded};
40             }
41             }
42              
43 12 100       54 if ( my $alias = $role_args->{'-alias'} ) {
44 1         11 for my $oldname ( sort keys %$alias ) {
45 1         4 my $newname = $alias->{$oldname};
46 1         14 $methods{$newname} = delete $methods{$oldname};
47             }
48             }
49              
50 12         54 return \%methods;
51             }
52              
53             sub accessors_to_export {
54 1     1 0 3 my $self = shift;
55 1 50       6 return {} unless $self->arg->{'-runtime'};
56              
57             my @accessors = map $_->associated_methods,
58 0         0 sort { $a->_order <=> $b->_order }
59 1         4 values %{ $self->attributes };
  1         10  
60              
61 1         4 return { map { $_ => $self->name . "::$_"; } @accessors };
  4         39  
62             }
63              
64             around compilation_stages => sub {
65             my ( $next, $self ) = ( shift, shift );
66             my @stages = $self->$next( @_ );
67             push @stages, qw(
68             _compile_callback
69             );
70             push @stages, '_compile_runtime_application'
71             if $self->arg->{'-runtime'};
72             return @stages;
73             };
74              
75             sub _compile_runtime_application {
76 1     1   3 my $self = shift;
77 1         4 my $name = $self->name;
78              
79             my $methods = {
80 1         4 %{ $self->methods_to_export },
81 1         2 %{ $self->accessors_to_export },
  1         16  
82             };
83             my $method_hash = join qq{,\n},
84             map sprintf(
85             ' %s => %s',
86             B::perlstring( $_ ),
87 1 50       96 B::perlstring( $methods->{$_} =~ /^\Q$name\E::(\w+)$/ ? $1 : $methods->{$_} )
88             ),
89             sort keys %$methods;
90              
91 1         40 return sprintf <<'CODE', $method_hash;
92             {
93             our ( %%METHODS ) = (
94             %s
95             );
96              
97             my %%DONE;
98             sub APPLY_TO {
99             my $to = shift;
100             if ( ref $to ) {
101             my $new_class = CREATE_CLASS( ref $to );
102             return bless( $to, $new_class );
103             }
104             return if $DONE{$to};
105             {
106             no strict 'refs';
107             ${"$to\::USES_MITE"} = 'Mite::Class';
108             for my $method ( keys %%METHODS ) {
109             $to->can($method) or *{"$to\::$method"} = \&{ $METHODS{$method} };
110             }
111             for ( "DOES", "does" ) {
112             $to->can( $_ ) or *{"$to\::$_"} = sub { shift->isa( @_ ) };
113             }
114             }
115             __PACKAGE__->__FINALIZE_APPLICATION__( $to );
116             $MITE_SHIM->HANDLE_around( $to, "class", [ "DOES", "does" ], sub {
117             my ( $next, $self, $role ) = @_;
118             return 1 if $role eq __PACKAGE__;
119             return 1 if $role eq $to;
120             return $self->$next( $role );
121             } );
122             $DONE{$to}++;
123             return;
124             }
125              
126             sub CREATE_CLASS {
127             my $base = shift;
128             my $new_class = "$base\::__WITH__::" . __PACKAGE__;
129             {
130             no strict 'refs';
131             @{"$new_class\::ISA"} = $base;
132             }
133             APPLY_TO( $new_class );
134             return $new_class;
135             }
136             }
137             CODE
138             }
139              
140             sub _compile_callback {
141 10     10   28 my $self = shift;
142              
143 10         26 my @required = @{ $self->required_methods };
  10         53  
144 10         18 my %uniq; undef $uniq{$_} for @required;
  10         43  
145 10         48 @required = sort keys %uniq;
146              
147 10         25 my $role_list = join q[, ], map B::perlstring( $_->name ), @{ $self->roles };
  10         54  
148             my $shim = B::perlstring(
149             $self->shim_name
150 10   50     52 || eval { $self->project->config->data->{shim} }
151             || 'Mite::Shim'
152             );
153 10         76 my $croak = $self->_function_for_croak;
154 10         74 my $missing_methods = '()';
155 10 100       38 if ( @required ) {
156 2         10 require B;
157 2         15 $missing_methods = sprintf 'grep( !$target->can($_), %s )',
158             join q[, ], map B::perlstring( $_ ), @required;
159             }
160              
161 10         284 return sprintf <<'CODE', $missing_methods, $croak, $role_list, $croak, $shim;
162             # Callback which classes consuming this role will call
163             sub __FINALIZE_APPLICATION__ {
164             my ( $me, $target, $args ) = @_;
165             our ( %%CONSUMERS, @METHOD_MODIFIERS );
166              
167             # Ensure a given target only consumes this role once.
168             if ( exists $CONSUMERS{$target} ) {
169             return;
170             }
171             $CONSUMERS{$target} = 1;
172              
173             my $type = do { no strict 'refs'; ${"$target\::USES_MITE"} };
174             return if $type ne 'Mite::Class';
175              
176             my @missing_methods;
177             @missing_methods = %s
178             and %s( "$me requires $target to implement methods: " . join q[, ], @missing_methods );
179              
180             my @roles = ( %s );
181             my %%nextargs = %%{ $args || {} };
182             ( $nextargs{-indirect} ||= 0 )++;
183             %s( "PANIC!" ) if $nextargs{-indirect} > 100;
184             for my $role ( @roles ) {
185             $role->__FINALIZE_APPLICATION__( $target, { %%nextargs } );
186             }
187              
188             my $shim = %s;
189             for my $modifier_rule ( @METHOD_MODIFIERS ) {
190             my ( $modification, $names, $coderef ) = @$modifier_rule;
191             my $handler = "HANDLE_$modification";
192             $shim->$handler( $target, "class", $names, $coderef );
193             }
194              
195             return;
196             }
197             CODE
198             }
199              
200             sub _needs_accessors {
201 10     10   30 my $self = shift;
202 10 100       131 $self->arg->{'-runtime'} ? true : false;
203             }
204              
205             sub _mop_metaclass {
206 0     0     return '$META_ROLE';
207             }
208              
209             sub _mop_attribute_metaclass {
210 0     0     return 'Moose::Meta::Role::Attribute';
211             }
212              
213             sub _compile_mop_modifiers {
214 0     0     my $self = shift;
215              
216 0           return sprintf <<'CODE', $self->name;
217             for ( @%s::METHOD_MODIFIERS ) {
218             my ( $type, $names, $code ) = @$_;
219             $PACKAGE->${\"add_$type\_method_modifier"}( $_, $code ) for @$names;
220             }
221             CODE
222             }
223              
224             sub _compile_mop_tc {
225 0     0     return sprintf ' Moose::Util::TypeConstraints::find_or_create_does_type_constraint( %s );',
226             B::perlstring( shift->name );
227             }
228              
229             1;