File Coverage

blib/lib/Minions.pm
Criterion Covered Total %
statement 332 356 93.2
branch 111 142 78.1
condition 33 51 64.7
subroutine 66 75 88.0
pod 1 2 50.0
total 543 626 86.7


line stmt bran cond sub pod time code
1             package Minions;
2              
3 48     48   3399107 use strict;
  48         115  
  48         1222  
4 48     48   1185 use 5.008_005;
  48         169  
5 48     48   240 use Carp;
  48         84  
  48         3392  
6 48     48   41519 use Hash::Util qw( lock_keys );
  48         142036  
  48         304  
7 48     48   44988 use List::MoreUtils qw( all );
  48         583813  
  48         361  
8 48     48   162852 use Module::Runtime qw( require_module );
  48         79298  
  48         345  
9 48     48   42494 use Params::Validate qw(:all);
  48         408000  
  48         10913  
10 48     48   33490 use Package::Stash;
  48         105040  
  48         1639  
11 48     48   306 use Sub::Name;
  48         92  
  48         2987  
12              
13             use Exception::Class (
14 48         509 'Minions::Error::AssertionFailure' => { alias => 'assert_failed' },
15             'Minions::Error::InterfaceMismatch',
16             'Minions::Error::MethodDeclaration',
17             'Minions::Error::RoleConflict',
18 48     48   12054 );
  48         180813  
19 48     48   64407 use Minions::_Guts;
  48         127  
  48         237889  
20              
21             our $VERSION = '0.000007';
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   4250 my ($class, %arg) = @_;
31              
32 23 100       134 if ( my $bindings = $arg{bind} ) {
    100          
33              
34 4         20 foreach my $class ( keys %$bindings ) {
35 4         122 $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         20 $Interface_for{$caller_pkg} = $methods;
41             }
42             else {
43 18         79 $class->minionize(\%arg);
44             }
45             }
46              
47             sub minionize {
48 53     53 1 2497 my (undef, $spec) = @_;
49              
50 53         110 my $cls_stash;
51 53 100       266 if ( ! $spec->{name} ) {
52 51         295 my $caller_pkg = (caller)[0];
53              
54 51 100       1099 if ( $caller_pkg eq __PACKAGE__ ) {
55 18         84 $caller_pkg = (caller 1)[0];
56             }
57 51         1490 $cls_stash = Package::Stash->new($caller_pkg);
58 51 100       187 $spec = { %$spec, %{ $cls_stash->get_symbol('%__meta__') || {} } };
  51         1669  
59 51         236 $spec->{name} = $caller_pkg;
60             }
61 53   33     222 $spec->{name} ||= "Minions::Class_${\ ++$Class_count }";
  0         0  
62              
63 53         233 my @args = %$spec;
64 53         3808 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     704 $cls_stash ||= Package::Stash->new($spec->{name});
74              
75 53         97 my $obj_stash;
76              
77 53 100       618 if ( ! ref $spec->{implementation} ) {
78 49   66     281 my $pkg = $Bound_implementation_of{ $spec->{name} } || $spec->{implementation};
79             $pkg ne $spec->{name}
80 49 50       199 or confess "$spec->{name} cannot be its own implementation.";
81 49         181 my $stash = _get_stash($pkg);
82              
83 49         317 my $meta = $stash->get_symbol('%__meta__');
84             $spec->{implementation} = {
85             package => $pkg,
86             methods => $stash->get_all_symbols('CODE'),
87             has => {
88 49 100       531 %{ $meta->{has} || { } },
  49         509  
89             },
90             };
91 49         164 $spec->{roles} = $meta->{roles};
92 49         187 my $is_semiprivate = _interface($meta, 'semiprivate');
93              
94 49         124 foreach my $sub ( keys %{ $spec->{implementation}{methods} } ) {
  49         311  
95 60 100       274 if ( $is_semiprivate->{$sub} ) {
96 7         117 $spec->{implementation}{semiprivate}{$sub} = delete $spec->{implementation}{methods}{$sub};
97             }
98             }
99             }
100 53         492 $obj_stash = Package::Stash->new("$spec->{name}::__Minions");
101              
102 53         215 _prep_interface($spec);
103 53         172 _compose_roles($spec);
104              
105 49         459 my $private_stash = Package::Stash->new("$spec->{name}::__Private");
106 49         608 $cls_stash->add_symbol('$__Obj_pkg', $obj_stash->name);
107 49         420 $cls_stash->add_symbol('$__Private_pkg', $private_stash->name);
108 49 50       461 $cls_stash->add_symbol('%__meta__', $spec) if @_ > 0;
109              
110 49         212 _make_util_class($spec);
111 49         351 _add_class_methods($spec, $cls_stash);
112 49         204 _add_methods($spec, $obj_stash, $private_stash);
113 49         187 _check_role_requirements($spec);
114 47         173 _check_interface($spec);
115 46         734 return $spec->{name};
116             }
117              
118             sub utility_class {
119 67     67 0 4185 my ($class) = @_;
120              
121 67 0       244 return $Util_class{ $class }
122             or confess "Unknown class: $class";
123             }
124              
125             sub _prep_interface {
126 53     53   124 my ($spec) = @_;
127              
128 53 100       320 return if ref $spec->{interface};
129 1         2 my $count = 0;
130             {
131              
132 1 100       1 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       4 $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   184 my ($spec, $roles, $from_role) = @_;
148              
149 83 100       285 if ( ! $roles ) {
150 53         127 $roles = $spec->{roles};
151             }
152              
153 83   100     387 $from_role ||= {};
154              
155 83         125 for my $role ( @{ $roles } ) {
  83         258  
156              
157 30 50       120 if ( $spec->{composed_role}{$role} ) {
158 0         0 confess "Cannot compose role '$role' twice";
159             }
160             else {
161 30         93 $spec->{composed_role}{$role}++;
162             }
163              
164 30         94 my ($meta, $method) = _load_role($role);
165 30         101 $spec->{required}{$role} = $meta->{requires};
166 30   100     267 _compose_roles($spec, $meta->{roles} || [], $from_role);
167              
168 29         123 _add_role_items($spec, $from_role, $role, $meta->{has}, 'has');
169 28         106 _add_role_methods($spec, $from_role, $role, $meta, $method);
170             }
171             }
172              
173             sub _load_role {
174 31     31   53 my ($role) = @_;
175              
176 31         84 my $stash = _get_stash($role);
177 31         161 my $meta = $stash->get_symbol('%__meta__');
178             $meta->{role}
179 31 50       119 or confess "$role is not a role";
180              
181 31         201 my $method = $stash->get_all_symbols('CODE');
182 31         130 return ($meta, $method);
183             }
184              
185             sub _check_role_requirements {
186 49     49   104 my ($spec) = @_;
187              
188 49         85 foreach my $role ( keys %{ $spec->{required} } ) {
  49         223  
189              
190 21         46 my $required = $spec->{required}{$role};
191              
192 21         37 foreach my $name ( @{ $required->{methods} } ) {
  21         93  
193              
194 3 100 100     23 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         38 foreach my $name ( @{ $required->{attributes} } ) {
  20         77  
201 2 100       36 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   97 my ($spec) = @_;
209 47         91 my $count = 0;
210 47         96 foreach my $method ( @{ $spec->{interface} } ) {
  47         193  
211 106 100       418 defined $spec->{implementation}{methods}{$method}
212             or confess "Interface method '$method' is not implemented.";
213 105         235 ++$count;
214             }
215 46 50       215 $count > 0 or confess "Cannot have an empty interface.";
216             }
217              
218             sub _get_stash {
219 81     81   157 my $pkg = shift;
220              
221 81         582 my $stash = Package::Stash->new($pkg); # allow for inlined pkg
222              
223 81 100       999 if ( ! $stash->has_symbol('%__meta__') ) {
224 29         150 require_module($pkg);
225 29         452 $stash = Package::Stash->new($pkg);
226             }
227 81 50       739 if ( ! $stash->has_symbol('%__meta__') ) {
228 0         0 confess "Package $pkg has no %__meta__";
229             }
230 81         240 return $stash;
231             }
232              
233             sub _add_role_items {
234 29     29   82 my ($spec, $from_role, $role, $item, $type) = @_;
235              
236 29         95 for my $name ( keys %$item ) {
237 11 100       45 if (my $other_role = $from_role->{$name}) {
238 1         4 _raise_role_conflict($name, $role, $other_role);
239             }
240             else{
241 10 100       63 if ( ! $spec->{implementation}{$type}{$name} ) {
242 7         20 $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   69 my ($spec, $from_role, $role, $role_meta, $code_for) = @_;
251              
252 28         80 my $in_class_interface = _interface($spec);
253 28         147 my $in_role_interface = _interface($role_meta);
254 28         100 my $is_semiprivate = _interface($role_meta, 'semiprivate');
255              
256 28 100   114   580 all { defined $in_class_interface->{$_} } keys %$in_role_interface
  114         281  
257             or Minions::Error::InterfaceMismatch->throw(
258             error => "Interfaces do not match: Class => $spec->{name}, Role => $role"
259             );
260              
261 27         156 for my $name ( keys %$code_for ) {
262 42 100 33     240 if ( $in_role_interface->{$name}
    50          
263             || $in_class_interface->{$name}
264             ) {
265 40 100       139 if (my $other_role = $from_role->{method}{$name}) {
266 2         8 _raise_role_conflict($name, $role, $other_role);
267             }
268 38 100       134 if ( ! $spec->{implementation}{methods}{$name} ) {
269 36         73 $spec->{implementation}{methods}{$name} = $code_for->{$name};
270 36         237 $from_role->{method}{$name} = $role;
271             }
272             }
273             elsif ( $is_semiprivate->{$name} ) {
274 2 50       8 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         43 $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         64 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   184 my ($utility_class, $init) = @_;
        65      
        10      
297              
298 65         284 my $class = $utility_class->main_class;
299              
300 65         718 my $stash = Package::Stash->new($class);
301              
302 65         648 my $spec = $stash->get_symbol('%__meta__');
303 65         299 my $pkg_key = Minions::_Guts::obfu_name('', $spec);
304             my %obj = (
305 65         134 $pkg_key => ${ $stash->get_symbol('$__Private_pkg') },
  65         481  
306             );
307              
308 65         188 while ( my ($attr, $meta) = each %{ $spec->{implementation}{has} } ) {
  150         837  
309 85         339 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       602 : $meta->{default});
    100          
315             }
316              
317 65         135 bless \ %obj => ${ $stash->get_symbol('$__Obj_pkg') };
  65         464  
318 65         349 lock_keys(%obj);
319 65         1094 return \ %obj;
320 49     49   445 };
321             }
322              
323             sub _add_class_methods {
324 49     49   119 my ($spec, $stash) = @_;
325              
326 49   66     1326 $spec->{class_methods} ||= $stash->get_all_symbols('CODE');
327 49         173 _add_default_constructor($spec);
328              
329 49         91 foreach my $sub ( keys %{ $spec->{class_methods} } ) {
  49         248  
330 442         2255 $stash->add_symbol("&$sub", $spec->{class_methods}{$sub});
331 442         2805 subname "$spec->{name}::$sub", $spec->{class_methods}{$sub};
332             }
333             }
334              
335             sub _make_util_class {
336 49     49   113 my ($spec) = @_;
337              
338 49         461 my $stash = Package::Stash->new("$spec->{name}::__Util");
339 49         249 $Util_class{ $spec->{name} } = $stash->name;
340              
341 49         198 my %method = (
342             new_object => _get_object_maker(),
343             );
344              
345 49     65   248 $method{main_class} = sub { $spec->{name} };
  65     65   183  
        10      
346              
347 49         266 my $obfu_pkg = Minions::_Guts::obfu_name('', $spec);
348             $method{build} = sub {
349 47     47   127 my (undef, $obj, $arg) = @_;
        47      
        10      
350 47 100       708 if ( my $builder = $obj->{$obfu_pkg}->can('BUILD') ) {
351 9         37 $builder->($obj->{$obfu_pkg}, $obj, $arg);
352             }
353 49         367 };
354              
355             $method{assert} = sub {
356 34     34   106 my (undef, $slot, $val) = @_;
        34      
        4      
357              
358 34 50       151 return unless exists $spec->{construct_with}{$slot};
359              
360 34         73 my $meta = $spec->{construct_with}{$slot};
361              
362 34 100       59 for my $desc ( keys %{ $meta->{assert} || {} } ) {
  34         204  
363 25         51 my $code = $meta->{assert}{$desc};
364 25 100       111 $code->($val)
365             or assert_failed error => "Parameter '$slot' failed check '$desc'";
366             }
367 49         357 };
368              
369 49         473 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         274 };
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         241 };
380              
381 49         194 foreach my $sub ( keys %method ) {
382 294         2568 $stash->add_symbol("&$sub", $method{$sub});
383 294         2810 subname $stash->name."::$sub", $method{$sub};
384             }
385             }
386              
387             sub _add_default_constructor {
388 49     49   111 my ($spec) = @_;
389              
390 49 100       285 if ( ! exists $spec->{class_methods}{new} ) {
391             $spec->{class_methods}{new} = sub {
392 63     63   20359 my $class = shift;
        58      
        10      
393 63         111 my ($arg);
394              
395 63 100       353 if ( scalar @_ == 1 ) {
    100          
396 5         10 $arg = shift;
397             }
398             elsif ( scalar @_ > 1 ) {
399 26         80 $arg = { @_ };
400             }
401 63 50       272 if (my @unknown = grep { ! exists $spec->{construct_with}{$_} } keys %$arg) {
  33         207  
402 0         0 confess "Unknown args: [@unknown]";
403             }
404              
405 63         200 my $utility_class = utility_class($class);
406 63         292 my $obj = $utility_class->new_object;
407 63         129 for my $name ( keys %{ $spec->{construct_with} } ) {
  63         255  
408              
409 45 100 66     267 if ( ! $spec->{construct_with}{$name}{optional} && ! defined $arg->{$name} ) {
410 7         156 confess "Param '$name' was not provided.";
411             }
412 38 100       144 if ( defined $arg->{$name} ) {
413 30         119 $utility_class->assert($name, $arg->{$name});
414             }
415              
416 48         168 my ($attr, $dup) = grep { $spec->{implementation}{has}{$_}{init_arg} eq $name }
417 29         191 keys %{ $spec->{implementation}{has} };
  29         96  
418 29 50       153 if ( $dup ) {
419 0         0 confess "Cannot have same init_arg '$name' for attributes '$attr' and '$dup'";
420             }
421 29 100       123 if ( $attr ) {
422 20         68 _copy_assertions($spec, $name, $attr);
423 20         49 my $sub = $spec->{implementation}{has}{$attr}{map_init_arg};
424 20         104 my $obfu_name = Minions::_Guts::obfu_name($attr, $spec) ;
425 20 100       107 $obj->{$obfu_name} = $sub ? $sub->($arg->{$name}) : $arg->{$name};
426             }
427             }
428              
429 47         232 $utility_class->build($obj, $arg);
430 45         268 return $obj;
431 47         386 };
432              
433 47   66     268 my $build_args = $spec->{build_args} || $spec->{class_methods}{BUILDARGS};
434 47 100       216 if ( $build_args ) {
435 3         12 my $prev_new = $spec->{class_methods}{new};
436              
437             $spec->{class_methods}{new} = sub {
438 5     5   2413 my $class = shift;
        41      
439 5         24 $prev_new->($class, $build_args->($class, @_));
440 3         18 };
441             }
442             }
443             }
444              
445             sub _copy_assertions {
446 20     20   55 my ($spec, $name, $attr) = @_;
447              
448 20         48 my $meta = $spec->{construct_with}{$name};
449              
450 20 100       42 for my $desc ( keys %{ $meta->{assert} || {} } ) {
  20         135  
451 14 100       75 next if exists $spec->{implementation}{has}{$attr}{assert}{$desc};
452              
453 11         51 $spec->{implementation}{has}{$attr}{assert}{$desc} = $meta->{assert}{$desc};
454             }
455             }
456              
457             sub _add_methods {
458 49     49   152 my ($spec, $stash, $private_stash) = @_;
459              
460 49         143 my $in_interface = _interface($spec);
461              
462             $spec->{implementation}{semiprivate}{ASSERT} = sub {
463 3     3   42 my (undef, $slot, $val) = @_;
        0      
        3      
464              
465 3 50       9 return unless exists $spec->{implementation}{has}{$slot};
466              
467 3         7 my $meta = $spec->{implementation}{has}{$slot};
468              
469 3 50       4 for my $desc ( keys %{ $meta->{assert} || {} } ) {
  3         10  
470 3         4 my $code = $meta->{assert}{$desc};
471 3 100       8 $code->($val)
472             or assert_failed error => "Attribute '$slot' failed check '$desc'";
473             }
474 49         453 };
475             $spec->{implementation}{methods}{DOES} = sub {
476 12     12   3495 my ($self, $r) = @_;
        0      
        12      
477              
478 12 100       35 if ( ! $r ) {
479             my @items = (( $spec->{interface_name} ? $spec->{interface_name} : () ),
480 1 50       5 $spec->{name}, sort keys %{ $spec->{composed_role} });
  1         9  
481 1 50       5 return unless defined wantarray;
482 1 50       19 return wantarray ? @items : \@items;
483             }
484              
485             return $r eq $spec->{interface_name}
486             || $spec->{name} eq $r
487 11   66     124 || $spec->{composed_role}{$r}
488             || $self->isa($r);
489 49         270 };
490             $spec->{implementation}{methods}{can} = sub {
491 17     17   1738 my ($self, $f) = @_;
        17      
        12      
492              
493 17 50       52 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         99 return UNIVERSAL::can($self, $f);
499 49         317 };
500 49         218 _add_autoload($spec, $stash);
501              
502 49         98 while ( my ($name, $meta) = each %{ $spec->{implementation}{has} } ) {
  99         548  
503              
504 50 50 33     415 if ( ! $spec->{implementation}{methods}{$name}
      66        
505             && $meta->{reader}
506             && $in_interface->{$name} ) {
507              
508 7 50       28 my $name = $meta->{reader} == 1 ? $name : $meta->{reader};
509 7         26 my $obfu_name = Minions::_Guts::obfu_name($name, $spec);
510 7     9   40 $spec->{implementation}{methods}{$name} = sub { $_[0]->{$obfu_name} };
  9     24   74  
511             }
512              
513 50 0 66     303 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         213 _add_delegates($spec, $meta, $name);
528             }
529              
530 49         146 while ( my ($name, $sub) = each %{ $spec->{implementation}{methods} } ) {
  306         1289  
531 257 100       623 next unless $in_interface->{$name};
532 255         3855 $stash->add_symbol("&$name", subname $stash->name."::$name" => $sub);
533             }
534 49         122 while ( my ($name, $sub) = each %{ $spec->{implementation}{semiprivate} } ) {
  106         559  
535 57         1232 $private_stash->add_symbol("&$name", subname $private_stash->name."::$name" => $sub);
536             }
537             }
538              
539             sub _add_autoload {
540 49     49   134 my ($spec, $stash) = @_;
541              
542             $spec->{implementation}{methods}{AUTOLOAD} = sub {
543 5     5   8035 my $self = shift;
        1      
        5      
544              
545 5         27 my $caller_sub = (caller 1)[3];
546 5         426 my $caller_pkg = $caller_sub;
547 5         39 $caller_pkg =~ s/::[^:]+$//;
548              
549 5         13 my $called = ${ $stash->get_symbol('$AUTOLOAD') };
  5         46  
550 5         35 $called =~ s/.+:://;
551              
552 5 100 66     74 if( exists $spec->{implementation}{semiprivate}{$called}
553             && $caller_pkg eq ref $self
554             ) {
555 1         7 my $stash = _get_stash($spec->{implementation}{package});
556 1         3 my $sp_var = ${ $stash->get_symbol('$__') };
  1         11  
557 1         28 return $self->{$sp_var}->$called($self, @_);
558             }
559             else {
560 4         103 croak sprintf(q{Can't locate object method "%s" via package "%s"},
561             $called, ref $self);
562             }
563 49         330 };
564             }
565              
566             sub _add_delegates {
567 50     50   112 my ($spec, $meta, $name) = @_;
568              
569 50 100       261 if ( $meta->{handles} ) {
570 4         7 my $method;
571 4         9 my $target_method = {};
572 4 100       33 if ( ref $meta->{handles} eq 'ARRAY' ) {
    100          
    50          
573 2         4 $method = { map { $_ => 1 } @{ $meta->{handles} } };
  6         14  
  2         5  
574             }
575             elsif( ref $meta->{handles} eq 'HASH' ) {
576 1         2 $method = $meta->{handles};
577 1         2 $target_method = $method;
578             }
579             elsif( ! ref $meta->{handles} ) {
580 1         3 (undef, $method) = _load_role($meta->{handles});
581             }
582 4         13 my $in_interface = _interface($spec);
583 4         24 my $obfu_name = Minions::_Guts::obfu_name($name, $spec);
584              
585 4         39 foreach my $meth ( keys %{ $method } ) {
  4         14  
586 14 50       45 if ( defined $spec->{implementation}{methods}{$meth} ) {
587 0         0 confess "Cannot override implemented method '$meth' with a delegated method";
588             }
589             else {
590 14   66     57 my $target = $target_method->{$meth} || $meth;
591             $spec->{implementation}{methods}{$meth} =
592             $in_interface->{$meth}
593 24     24   7972 ? sub { shift->{$obfu_name}->$target(@_) }
        12      
        4      
594 14 50   0   98 : sub { shift; shift->{$obfu_name}->$target(@_) };
  0         0  
  0         0  
595             }
596             }
597             }
598             }
599              
600             sub _interface {
601 186     186   353 my ($spec, $type) = @_;
602              
603 186   100     752 $type ||= 'interface';
604 186         823 my %must_allow = (
605             interface => [qw( AUTOLOAD can DOES DESTROY )],
606             semiprivate => [qw( BUILD )],
607             );
608 186         302 return { map { $_ => 1 } @{ $spec->{$type} }, @{ $must_allow{$type} } };
  713         1939  
  186         432  
  186         408  
609             }
610              
611             1;
612             __END__