File Coverage

blib/lib/Minions.pm
Criterion Covered Total %
statement 332 357 93.0
branch 112 144 77.7
condition 33 51 64.7
subroutine 66 75 88.0
pod 1 2 50.0
total 544 629 86.4


line stmt bran cond sub pod time code
1             package Minions;
2              
3 48     48   2963599 use strict;
  48         111  
  48         1157  
4 48     48   1139 use 5.008_005;
  48         166  
5 48     48   230 use Carp;
  48         90  
  48         3106  
6 48     48   46389 use Hash::Util qw( lock_keys );
  48         141950  
  48         296  
7 48     48   44689 use List::MoreUtils qw( all );
  48         581503  
  48         351  
8 48     48   67784 use Module::Runtime qw( require_module );
  48         77239  
  48         335  
9 48     48   41062 use Params::Validate qw(:all);
  48         397975  
  48         10387  
10 48     48   32774 use Package::Stash;
  48         102625  
  48         1592  
11 48     48   284 use Sub::Name;
  48         96  
  48         2903  
12              
13             use Exception::Class (
14 48         540 'Minions::Error::AssertionFailure' => { alias => 'assert_failed' },
15             'Minions::Error::InterfaceMismatch',
16             'Minions::Error::MethodDeclaration',
17             'Minions::Error::RoleConflict',
18 48     48   11797 );
  48         177968  
19 48     48   60833 use Minions::_Guts;
  48         117  
  48         234808  
20              
21             our $VERSION = '0.000008';
22             $VERSION = eval $VERSION;
23              
24             my $Class_count = 0;
25             my %Bound_implementation_of;
26             my %Interface_for;
27             my %Util_class;
28              
29             sub import {
30 23     23   4022 my ($class, %arg) = @_;
31              
32 23 100       120 if ( my $bindings = $arg{bind} ) {
    100          
33              
34 4         16 foreach my $class ( keys %$bindings ) {
35 4         113 $Bound_implementation_of{$class} = $bindings->{$class};
36             }
37             }
38             elsif ( my $methods = $arg{declare_interface} ) {
39 1         3 my $caller_pkg = (caller)[0];
40 1         21 $Interface_for{$caller_pkg} = $methods;
41             }
42             else {
43 18         72 $class->minionize(\%arg);
44             }
45             }
46              
47             sub minionize {
48 53     53 1 2232 my (undef, $spec) = @_;
49              
50 53         98 my $cls_stash;
51 53 100       244 if ( ! $spec->{name} ) {
52 51         252 my $caller_pkg = (caller)[0];
53              
54 51 100       1042 if ( $caller_pkg eq __PACKAGE__ ) {
55 18         76 $caller_pkg = (caller 1)[0];
56             }
57 51         1377 $cls_stash = Package::Stash->new($caller_pkg);
58 51 100       183 $spec = { %$spec, %{ $cls_stash->get_symbol('%__meta__') || {} } };
  51         1658  
59 51         228 $spec->{name} = $caller_pkg;
60             }
61 53   33     204 $spec->{name} ||= "Minions::Class_${\ ++$Class_count }";
  0         0  
62              
63 53         231 my @args = %$spec;
64 53         3712 validate(@args, {
65             interface => { type => ARRAYREF | SCALAR },
66             implementation => { type => SCALAR | HASHREF },
67             construct_with => { type => HASHREF, optional => 1 },
68             class_methods => { type => HASHREF, optional => 1 },
69             build_args => { type => CODEREF, optional => 1 },
70             name => { type => SCALAR, optional => 1 },
71             no_attribute_vars => { type => BOOLEAN, optional => 1 },
72             });
73 53   66     673 $cls_stash ||= Package::Stash->new($spec->{name});
74              
75 53         95 my $obj_stash;
76              
77 53 100       594 if ( ! ref $spec->{implementation} ) {
78 49   66     265 my $pkg = $Bound_implementation_of{ $spec->{name} } || $spec->{implementation};
79             $pkg ne $spec->{name}
80 49 50       189 or confess "$spec->{name} cannot be its own implementation.";
81 49         169 my $stash = _get_stash($pkg);
82              
83 49         302 my $meta = $stash->get_symbol('%__meta__');
84             $spec->{implementation} = {
85             package => $pkg,
86             methods => $stash->get_all_symbols('CODE'),
87             has => {
88 49 100       487 %{ $meta->{has} || { } },
  49         472  
89             },
90             };
91 49         148 $spec->{roles} = $meta->{roles};
92 49         180 my $is_semiprivate = _interface($meta, 'semiprivate');
93              
94 49         131 foreach my $sub ( keys %{ $spec->{implementation}{methods} } ) {
  49         288  
95 60 100       279 if ( $is_semiprivate->{$sub} ) {
96 7         48 $spec->{implementation}{semiprivate}{$sub} = delete $spec->{implementation}{methods}{$sub};
97             }
98             }
99             }
100 53         468 $obj_stash = Package::Stash->new("$spec->{name}::__Minions");
101              
102 53         203 _prep_interface($spec);
103 53         168 _compose_roles($spec);
104              
105 49         473 my $private_stash = Package::Stash->new("$spec->{name}::__Private");
106 49         554 $cls_stash->add_symbol('$__Obj_pkg', $obj_stash->name);
107 49         405 $cls_stash->add_symbol('$__Private_pkg', $private_stash->name);
108 49 50       452 $cls_stash->add_symbol('%__meta__', $spec) if @_ > 0;
109              
110 49         192 _make_util_class($spec);
111 49         328 _add_class_methods($spec, $cls_stash);
112 49         205 _add_methods($spec, $obj_stash, $private_stash);
113 49         171 _check_role_requirements($spec);
114 47         145 _check_interface($spec);
115 46         646 return $spec->{name};
116             }
117              
118             sub utility_class {
119 67     67 0 2754 my ($class) = @_;
120              
121 67 0       201 return $Util_class{ $class }
122             or confess "Unknown class: $class";
123             }
124              
125             sub _prep_interface {
126 53     53   110 my ($spec) = @_;
127              
128 53 100       269 return if ref $spec->{interface};
129 1         1 my $count = 0;
130             {
131              
132 1 100       2 if (my $methods = $Interface_for{ $spec->{interface} }) {
  2         8  
133 1         2 $spec->{interface_name} = $spec->{interface};
134 1         3 $spec->{interface} = $methods;
135             }
136             else {
137 1 50       3 $count > 0
138             and confess "Invalid interface: $spec->{interface}";
139 1         4 require_module($spec->{interface});
140 1         7 $count++;
141 1         2 redo;
142             }
143             }
144             }
145              
146             sub _compose_roles {
147 83     83   175 my ($spec, $roles, $from_role) = @_;
148              
149 83 100       246 if ( ! $roles ) {
150 53         122 $roles = $spec->{roles};
151             }
152              
153 83   100     374 $from_role ||= {};
154              
155 83         108 for my $role ( @{ $roles } ) {
  83         258  
156              
157 30 50       118 if ( $spec->{composed_role}{$role} ) {
158 0         0 confess "Cannot compose role '$role' twice";
159             }
160             else {
161 30         88 $spec->{composed_role}{$role}++;
162             }
163              
164 30         87 my ($meta, $method) = _load_role($role);
165 30         95 $spec->{required}{$role} = $meta->{requires};
166 30   100     261 _compose_roles($spec, $meta->{roles} || [], $from_role);
167              
168 29         117 _add_role_items($spec, $from_role, $role, $meta->{has}, 'has');
169 28         100 _add_role_methods($spec, $from_role, $role, $meta, $method);
170             }
171             }
172              
173             sub _load_role {
174 31     31   64 my ($role) = @_;
175              
176 31         82 my $stash = _get_stash($role);
177 31         156 my $meta = $stash->get_symbol('%__meta__');
178             $meta->{role}
179 31 50       118 or confess "$role is not a role";
180              
181 31         188 my $method = $stash->get_all_symbols('CODE');
182 31         118 return ($meta, $method);
183             }
184              
185             sub _check_role_requirements {
186 49     49   116 my ($spec) = @_;
187              
188 49         83 foreach my $role ( keys %{ $spec->{required} } ) {
  49         205  
189              
190 21         48 my $required = $spec->{required}{$role};
191              
192 21         35 foreach my $name ( @{ $required->{methods} } ) {
  21         96  
193              
194 3 100 100     25 unless ( defined $spec->{implementation}{methods}{$name}
195             || defined $spec->{implementation}{semiprivate}{$name}
196             ) {
197 1         22 confess "Method '$name', required by role $role, is not implemented.";
198             }
199             }
200 20         36 foreach my $name ( @{ $required->{attributes} } ) {
  20         73  
201 2 100       28 defined $spec->{implementation}{has}{$name}
202             or confess "Attribute '$name', required by role $role, is not defined.";
203             }
204             }
205             }
206              
207             sub _check_interface {
208 47     47   96 my ($spec) = @_;
209 47         103 my $count = 0;
210 47         81 foreach my $method ( @{ $spec->{interface} } ) {
  47         180  
211 106 100       413 defined $spec->{implementation}{methods}{$method}
212             or confess "Interface method '$method' is not implemented.";
213 105         219 ++$count;
214             }
215 46 50       215 $count > 0 or confess "Cannot have an empty interface.";
216             }
217              
218             sub _get_stash {
219 81     81   144 my $pkg = shift;
220              
221 81         540 my $stash = Package::Stash->new($pkg); # allow for inlined pkg
222              
223 81 100       949 if ( ! $stash->has_symbol('%__meta__') ) {
224 29         140 require_module($pkg);
225 29         419 $stash = Package::Stash->new($pkg);
226             }
227 81 50       687 if ( ! $stash->has_symbol('%__meta__') ) {
228 0         0 confess "Package $pkg has no %__meta__";
229             }
230 81         226 return $stash;
231             }
232              
233             sub _add_role_items {
234 29     29   78 my ($spec, $from_role, $role, $item, $type) = @_;
235              
236 29         100 for my $name ( keys %$item ) {
237 11 100       48 if (my $other_role = $from_role->{$name}) {
238 1         5 _raise_role_conflict($name, $role, $other_role);
239             }
240             else{
241 10 100       45 if ( ! $spec->{implementation}{$type}{$name} ) {
242 7         22 $spec->{implementation}{$type}{$name} = $item->{$name};
243 7         22 $from_role->{$name} = $role;
244             }
245             }
246             }
247             }
248              
249             sub _add_role_methods {
250 28     28   71 my ($spec, $from_role, $role, $role_meta, $code_for) = @_;
251              
252 28         77 my $in_class_interface = _interface($spec);
253 28         143 my $in_role_interface = _interface($role_meta);
254 28         104 my $is_semiprivate = _interface($role_meta, 'semiprivate');
255              
256 28 100   113   514 all { defined $in_class_interface->{$_} } keys %$in_role_interface
  113         250  
257             or Minions::Error::InterfaceMismatch->throw(
258             error => "Interfaces do not match: Class => $spec->{name}, Role => $role"
259             );
260              
261 27         165 for my $name ( keys %$code_for ) {
262 42 100 33     228 if ( $in_role_interface->{$name}
    50          
263             || $in_class_interface->{$name}
264             ) {
265 40 100       137 if (my $other_role = $from_role->{method}{$name}) {
266 2         9 _raise_role_conflict($name, $role, $other_role);
267             }
268 38 100       124 if ( ! $spec->{implementation}{methods}{$name} ) {
269 36         78 $spec->{implementation}{methods}{$name} = $code_for->{$name};
270 36         194 $from_role->{method}{$name} = $role;
271             }
272             }
273             elsif ( $is_semiprivate->{$name} ) {
274 2 50       7 if (my $other_role = $from_role->{semiprivate}{$name}) {
275 0         0 _raise_role_conflict($name, $role, $other_role);
276             }
277 2 100       8 if ( ! $spec->{implementation}{semiprivate}{$name} ) {
278 1         3 $spec->{implementation}{semiprivate}{$name} = $code_for->{$name};
279 1         10 $from_role->{semiprivate}{$name} = $role;
280             }
281             }
282             }
283             }
284              
285             sub _raise_role_conflict {
286 3     3   7 my ($name, $role, $other_role) = @_;
287              
288 3         62 Minions::Error::RoleConflict->throw(
289             error => "Cannot have '$name' in both $role and $other_role"
290             );
291             }
292              
293             sub _get_object_maker {
294              
295             sub {
296 65     65   208 my ($utility_class, $init) = @_;
        65      
        10      
297              
298 65         227 my $class = $utility_class->main_class;
299              
300 65         644 my $stash = Package::Stash->new($class);
301              
302 65         654 my $spec = $stash->get_symbol('%__meta__');
303 65         289 my $pkg_key = Minions::_Guts::obfu_name('', $spec);
304             my %obj = (
305 65         134 $pkg_key => ${ $stash->get_symbol('$__Private_pkg') },
  65         452  
306             );
307              
308 65         190 while ( my ($attr, $meta) = each %{ $spec->{implementation}{has} } ) {
  150         814  
309 85         331 my $obfu_name = Minions::_Guts::obfu_name($attr, $spec);
310             $obj{$obfu_name} = $init->{$attr}
311             ? $init->{$attr}
312             : (ref $meta->{default} eq 'CODE'
313             ? $meta->{default}->()
314 85 100       586 : $meta->{default});
    100          
315             }
316              
317 65         117 bless \ %obj => ${ $stash->get_symbol('$__Obj_pkg') };
  65         461  
318 65         390 lock_keys(%obj);
319 65         1105 return \ %obj;
320 49     49   432 };
321             }
322              
323             sub _add_class_methods {
324 49     49   105 my ($spec, $stash) = @_;
325              
326 49   66     1221 $spec->{class_methods} ||= $stash->get_all_symbols('CODE');
327 49         180 _add_default_constructor($spec);
328              
329 49         94 foreach my $sub ( keys %{ $spec->{class_methods} } ) {
  49         250  
330 442         2125 $stash->add_symbol("&$sub", $spec->{class_methods}{$sub});
331 442         2818 subname "$spec->{name}::$sub", $spec->{class_methods}{$sub};
332             }
333             }
334              
335             sub _make_util_class {
336 49     49   112 my ($spec) = @_;
337              
338 49         394 my $stash = Package::Stash->new("$spec->{name}::__Util");
339 49         246 $Util_class{ $spec->{name} } = $stash->name;
340              
341 49         190 my %method = (
342             new_object => _get_object_maker(),
343             );
344              
345 49     65   227 $method{main_class} = sub { $spec->{name} };
  65     65   171  
        10      
346              
347 49         258 my $obfu_pkg = Minions::_Guts::obfu_name('', $spec);
348             $method{build} = sub {
349 47     47   123 my (undef, $obj, $arg) = @_;
        47      
        10      
350 47 100       721 if ( my $builder = $obj->{$obfu_pkg}->can('BUILD') ) {
351 9         34 $builder->($obj->{$obfu_pkg}, $obj, $arg);
352             }
353 49         312 };
354              
355             $method{assert} = sub {
356 36     36   104 my (undef, $slot, $val) = @_;
        36      
        4      
357              
358 36 50       148 return unless exists $spec->{construct_with}{$slot};
359              
360 36         70 my $meta = $spec->{construct_with}{$slot};
361              
362 36 100       62 for my $desc ( keys %{ $meta->{assert} || {} } ) {
  36         211  
363 25         42 my $code = $meta->{assert}{$desc};
364 25 100       98 $code->($val)
365             or assert_failed error => "Parameter '$slot' failed check '$desc'";
366             }
367 49         337 };
368              
369 49         440 my $class_var_stash = Package::Stash->new("$spec->{name}::__ClassVar");
370              
371             $method{get_var} = sub {
372 0     0   0 my ($class, $name) = @_;
        0      
        0      
373 0         0 $class_var_stash->get_symbol($name);
374 49         270 };
375              
376             $method{set_var} = sub {
377 0     0   0 my ($class, $name, $val) = @_;
        0      
        0      
378 0         0 $class_var_stash->add_symbol($name, $val);
379 49         229 };
380              
381 49         180 foreach my $sub ( keys %method ) {
382 294         2410 $stash->add_symbol("&$sub", $method{$sub});
383 294         2755 subname $stash->name."::$sub", $method{$sub};
384             }
385             }
386              
387             sub _add_default_constructor {
388 49     49   115 my ($spec) = @_;
389              
390 49 100       263 if ( ! exists $spec->{class_methods}{new} ) {
391             $spec->{class_methods}{new} = sub {
392 63     63   17711 my $class = shift;
        58      
        10      
393 63         104 my ($arg);
394              
395 63 100       350 if ( scalar @_ == 1 ) {
    100          
396 5         8 $arg = shift;
397             }
398             elsif ( scalar @_ > 1 ) {
399 26         90 $arg = { @_ };
400             }
401 63 50       257 if (my @unknown = grep { ! exists $spec->{construct_with}{$_} } keys %$arg) {
  33         207  
402 0         0 confess "Unknown args: [@unknown]";
403             }
404              
405 63         187 my $utility_class = utility_class($class);
406 63         296 my $obj = $utility_class->new_object;
407 63         116 for my $name ( keys %{ $spec->{construct_with} } ) {
  63         253  
408              
409 47 100 66     265 if ( ! $spec->{construct_with}{$name}{optional} && ! defined $arg->{$name} ) {
410 7         160 confess "Param '$name' was not provided.";
411             }
412 40 100       142 if ( defined $arg->{$name} ) {
413 32         129 $utility_class->assert($name, $arg->{$name});
414             }
415              
416 52         194 my ($attr, $dup) = grep { $spec->{implementation}{has}{$_}{init_arg} eq $name }
417 31         199 keys %{ $spec->{implementation}{has} };
  31         104  
418 31 50       127 if ( $dup ) {
419 0         0 confess "Cannot have same init_arg '$name' for attributes '$attr' and '$dup'";
420             }
421 31 100       115 if ( $attr ) {
422 19         75 _copy_assertions($spec, $name, $attr);
423 19         44 my $sub = $spec->{implementation}{has}{$attr}{map_init_arg};
424 19         71 my $obfu_name = Minions::_Guts::obfu_name($attr, $spec) ;
425 19 100       116 $obj->{$obfu_name} = $sub ? $sub->($arg->{$name}) : $arg->{$name};
426             }
427             }
428              
429 47         204 $utility_class->build($obj, $arg);
430 45         247 return $obj;
431 47         364 };
432              
433 47   66     259 my $build_args = $spec->{build_args} || $spec->{class_methods}{BUILDARGS};
434 47 100       196 if ( $build_args ) {
435 3         7 my $prev_new = $spec->{class_methods}{new};
436              
437             $spec->{class_methods}{new} = sub {
438 5     5   2066 my $class = shift;
        41      
439 5         25 $prev_new->($class, $build_args->($class, @_));
440 3         19 };
441             }
442             }
443             }
444              
445             sub _copy_assertions {
446 19     19   53 my ($spec, $name, $attr) = @_;
447              
448 19         48 my $meta = $spec->{construct_with}{$name};
449              
450 19 100       43 for my $desc ( keys %{ $meta->{assert} || {} } ) {
  19         156  
451 13 100       66 next if exists $spec->{implementation}{has}{$attr}{assert}{$desc};
452              
453 11         48 $spec->{implementation}{has}{$attr}{assert}{$desc} = $meta->{assert}{$desc};
454             }
455             }
456              
457             sub _add_methods {
458 49     49   142 my ($spec, $stash, $private_stash) = @_;
459              
460 49         122 my $in_interface = _interface($spec);
461              
462             $spec->{implementation}{semiprivate}{ASSERT} = sub {
463 3     3   37 my (undef, $slot, $val) = @_;
        0      
        3      
464              
465 3 50       9 return unless exists $spec->{implementation}{has}{$slot};
466              
467 3         6 my $meta = $spec->{implementation}{has}{$slot};
468              
469 3 50       5 for my $desc ( keys %{ $meta->{assert} || {} } ) {
  3         11  
470 3         4 my $code = $meta->{assert}{$desc};
471 3 100       9 $code->($val)
472             or assert_failed error => "Attribute '$slot' failed check '$desc'";
473             }
474 49         420 };
475             $spec->{implementation}{methods}{DOES} = sub {
476 12     12   2549 my ($self, $r) = @_;
        0      
        12      
477              
478 12 100       33 if ( ! $r ) {
479             my @items = (( $spec->{interface_name} ? $spec->{interface_name} : () ),
480 1 50       4 $spec->{name}, sort keys %{ $spec->{composed_role} });
  1         9  
481 1 50       4 return unless defined wantarray;
482 1 50       22 return wantarray ? @items : \@items;
483             }
484              
485             return $r eq $spec->{interface_name}
486             || $spec->{name} eq $r
487 11   66     114 || $spec->{composed_role}{$r}
488             || $self->isa($r);
489 49         261 };
490             $spec->{implementation}{methods}{can} = sub {
491 17     17   1431 my ($self, $f) = @_;
        17      
        12      
492              
493 17 50       53 if ( ! $f ) {
494 0         0 my @items = sort @{ $spec->{interface} };
  0         0  
495 0 0       0 return unless defined wantarray;
496 0 0       0 return wantarray ? @items : \@items;
497             }
498 17         85 return UNIVERSAL::can($self, $f);
499 49         307 };
500 49         220 _add_autoload($spec, $stash);
501              
502 49         87 while ( my ($name, $meta) = each %{ $spec->{implementation}{has} } ) {
  99         544  
503              
504 50 50 33     382 if ( ! $spec->{implementation}{methods}{$name}
      66        
505             && $meta->{reader}
506             && $in_interface->{$name} ) {
507              
508 7 50       30 my $name = $meta->{reader} == 1 ? $name : $meta->{reader};
509 7         26 my $obfu_name = Minions::_Guts::obfu_name($name, $spec);
510 7     9   43 $spec->{implementation}{methods}{$name} = sub { $_[0]->{$obfu_name} };
  9     24   83  
511             }
512              
513 50 0 66     353 if ( ! $spec->{implementation}{methods}{$name}
      33        
514             && $meta->{writer}
515             && $in_interface->{$name} ) {
516              
517 0         0 my $name = $meta->{writer};
518 0         0 my $obfu_pkg = Minions::_Guts::obfu_name('', $spec);
519             $spec->{implementation}{methods}{$name} = sub {
520 0     5   0 my ($self, $new_val) = @_;
521              
522 0         0 $self->{$obfu_pkg}->ASSERT($name, $new_val);
523 0         0 $self->{ Minions::_Guts::obfu_name($name, $spec) } = $new_val;
524 0         0 return $self;
525 0         0 };
526             }
527 50         215 _add_delegates($spec, $meta, $name);
528             }
529              
530 49         140 while ( my ($name, $sub) = each %{ $spec->{implementation}{methods} } ) {
  306         1246  
531 257 100       610 next unless $in_interface->{$name};
532 255         3698 $stash->add_symbol("&$name", subname $stash->name."::$name" => $sub);
533             }
534 49         113 while ( my ($name, $sub) = each %{ $spec->{implementation}{semiprivate} } ) {
  106         600  
535 57         1197 $private_stash->add_symbol("&$name", subname $private_stash->name."::$name" => $sub);
536             }
537             }
538              
539             sub _add_autoload {
540 49     49   104 my ($spec, $stash) = @_;
541              
542             $spec->{implementation}{methods}{AUTOLOAD} = sub {
543 5     5   6481 my $self = shift;
        1      
        5      
544              
545 5         23 my $caller_sub = (caller 1)[3];
546 5         330 my $caller_pkg = $caller_sub;
547 5         35 $caller_pkg =~ s/::[^:]+$//;
548              
549 5         9 my $called = ${ $stash->get_symbol('$AUTOLOAD') };
  5         37  
550 5         28 $called =~ s/.+:://;
551              
552 5 100 66     77 if( exists $spec->{implementation}{semiprivate}{$called}
    50          
553             && $caller_pkg eq ref $self
554             ) {
555 1         6 my $stash = _get_stash($spec->{implementation}{package});
556 1         2 my $sp_var = ${ $stash->get_symbol('$__') };
  1         8  
557 1         25 return $self->{$sp_var}->$called($self, @_);
558             }
559             elsif( $called eq 'DESTROY' ) {
560 0         0 return;
561             }
562             else {
563 4         92 croak sprintf(q{Can't locate object method "%s" via package "%s"},
564             $called, ref $self);
565             }
566 49         347 };
567             }
568              
569             sub _add_delegates {
570 50     50   115 my ($spec, $meta, $name) = @_;
571              
572 50 100       225 if ( $meta->{handles} ) {
573 4         7 my $method;
574 4         8 my $target_method = {};
575 4 100       29 if ( ref $meta->{handles} eq 'ARRAY' ) {
    100          
    50          
576 2         5 $method = { map { $_ => 1 } @{ $meta->{handles} } };
  6         15  
  2         4  
577             }
578             elsif( ref $meta->{handles} eq 'HASH' ) {
579 1         2 $method = $meta->{handles};
580 1         3 $target_method = $method;
581             }
582             elsif( ! ref $meta->{handles} ) {
583 1         4 (undef, $method) = _load_role($meta->{handles});
584             }
585 4         11 my $in_interface = _interface($spec);
586 4         19 my $obfu_name = Minions::_Guts::obfu_name($name, $spec);
587              
588 4         39 foreach my $meth ( keys %{ $method } ) {
  4         19  
589 14 50       77 if ( defined $spec->{implementation}{methods}{$meth} ) {
590 0         0 confess "Cannot override implemented method '$meth' with a delegated method";
591             }
592             else {
593 14   66     56 my $target = $target_method->{$meth} || $meth;
594             $spec->{implementation}{methods}{$meth} =
595             $in_interface->{$meth}
596 24     24   7429 ? sub { shift->{$obfu_name}->$target(@_) }
        12      
        4      
597 14 50   0   104 : sub { shift; shift->{$obfu_name}->$target(@_) };
  0         0  
  0         0  
598             }
599             }
600             }
601             }
602              
603             sub _interface {
604 186     186   329 my ($spec, $type) = @_;
605              
606 186   100     835 $type ||= 'interface';
607 186         821 my %must_allow = (
608             interface => [qw( AUTOLOAD can DOES DESTROY )],
609             semiprivate => [qw( BUILD )],
610             );
611 186         335 return { map { $_ => 1 } @{ $spec->{$type} }, @{ $must_allow{$type} } };
  713         1863  
  186         426  
  186         380  
612             }
613              
614             1;
615             __END__