File Coverage

blib/lib/Moo/Role.pm
Criterion Covered Total %
statement 201 204 98.5
branch 64 68 94.1
condition 80 96 83.3
subroutine 55 56 98.2
pod 3 5 60.0
total 403 429 93.9


line stmt bran cond sub pod time code
1             package Moo::Role;
2 126     158   6211179 use strict;
  126         340  
  126         3936  
3 126     150   746 use warnings;
  126         519  
  126         5053  
4              
5 126         10588 use Moo::_Utils qw(
6             _check_tracked
7             _getglob
8             _getstash
9             _install_coderef
10             _install_modifier
11             _install_tracked
12             _load_module
13             _name_coderef
14             _set_loaded
15             _unimport_coderefs
16 126     150   17952 );
  126         284  
17 126     134   804 use Carp qw(croak);
  126         291  
  126         4881  
18 126     126   64077 use Role::Tiny ();
  126         552592  
  126         4314  
19 126     126   7027 BEGIN { our @ISA = qw(Role::Tiny) }
20             BEGIN {
21 126     126   10890 our @CARP_NOT = qw(
22             Method::Generate::Accessor
23             Method::Generate::Constructor
24             Moo::sification
25             Moo::_Utils
26             Role::Tiny
27             );
28             }
29              
30             our $VERSION = '2.005003';
31             $VERSION =~ tr/_//d;
32              
33             require Moo::sification;
34             Moo::sification->import;
35              
36             BEGIN {
37 126     126   566 *INFO = \%Role::Tiny::INFO;
38 126         302 *APPLIED_TO = \%Role::Tiny::APPLIED_TO;
39 126         257 *COMPOSED = \%Role::Tiny::COMPOSED;
40 126         274132 *ON_ROLE_CREATE = \@Role::Tiny::ON_ROLE_CREATE;
41             }
42              
43             our %INFO;
44             our %APPLIED_TO;
45             our %APPLY_DEFAULTS;
46             our %COMPOSED;
47             our @ON_ROLE_CREATE;
48              
49             sub import {
50 192     192   958841 my $target = caller;
51 192 100 100     761 if ($Moo::MAKERS{$target} and $Moo::MAKERS{$target}{is_class}) {
52 4         568 croak "Cannot import Moo::Role into a Moo class";
53             }
54 188         965 _set_loaded(caller);
55 188         991 goto &Role::Tiny::import;
56             }
57              
58             sub _accessor_maker_for {
59 98     98   266 my ($class, $target) = @_;
60 98   66     513 ($INFO{$target}{accessor_maker} ||= do {
61 82         17571 require Method::Generate::Accessor;
62 82         1091 Method::Generate::Accessor->new
63             });
64             }
65              
66             sub _install_subs {
67 188     188   919 my ($me, $target) = @_;
68 188         568 my %install = $me->_gen_subs($target);
69             _install_tracked $target => $_ => $install{$_}
70 188         1836 for sort keys %install;
71 188         1447 *{_getglob("${target}::meta")} = $me->can('meta');
  188         715  
72 188         699 return;
73             }
74              
75             sub _require_module {
76 416     416   25472 _load_module($_[1]);
77             }
78              
79             sub _gen_subs {
80 188     188   425 my ($me, $target) = @_;
81             return (
82             has => sub {
83 100     100   297104 my $name_proto = shift;
        92      
        60      
        46      
84 100 100       529 my @name_proto = ref $name_proto eq 'ARRAY' ? @$name_proto : $name_proto;
85 100 100       472 if (@_ % 2 != 0) {
86 4         757 croak("Invalid options for " . join(', ', map "'$_'", @name_proto)
87             . " attribute(s): even number of arguments expected, got " . scalar @_)
88             }
89 96         432 my %spec = @_;
90 96         271 foreach my $name (@name_proto) {
91 98 100       360 my $spec_ref = @name_proto > 1 ? +{%spec} : \%spec;
92 98         585 $me->_accessor_maker_for($target)
93             ->generate_method($target, $name, $spec_ref);
94 98   100     221 push @{$INFO{$target}{attributes}||=[]}, $name, $spec_ref;
  98         729  
95 98         485 $me->_maybe_reset_handlemoose($target);
96             }
97             },
98             (map {
99 564         918 my $type = $_;
100             (
101             $type => sub {
102 20   100 20   13524 push @{$INFO{$target}{modifiers}||=[]}, [ $type => @_ ];
  20     20   153  
        20      
        10      
        30      
        30      
        20      
        40      
        40      
        30      
103 20         82 $me->_maybe_reset_handlemoose($target);
104             },
105             )
106 564         3813 } qw(before after around)),
107             requires => sub {
108 24   100 54   16086 push @{$INFO{$target}{requires}||=[]}, @_;
  24     44   185  
        42      
        38      
109 24         118 $me->_maybe_reset_handlemoose($target);
110             },
111             with => sub {
112 14     34   5160 $me->apply_roles_to_package($target, @_);
        28      
        26      
        12      
113 14         1359 $me->_maybe_reset_handlemoose($target);
114             },
115 188         1287 );
116             }
117              
118             push @ON_ROLE_CREATE, sub {
119             my $target = shift;
120             if ($INC{'Moo/HandleMoose.pm'} && !$Moo::sification::disabled) {
121             Moo::HandleMoose::inject_fake_metaclass_for($target);
122             }
123             };
124              
125             # duplicate from Moo::Object
126             sub meta {
127 2     12 0 19 require Moo::HandleMoose::FakeMetaClass;
128 2   33     13 my $class = ref($_[0])||$_[0];
129 2         32 bless({ name => $class }, 'Moo::HandleMoose::FakeMetaClass');
130             }
131              
132             sub unimport {
133 10     18   861 my $target = caller;
134 10         44 _unimport_coderefs($target);
135             }
136              
137             sub _maybe_reset_handlemoose {
138 156     164   403 my ($class, $target) = @_;
139 156 100 100     1815 if ($INC{'Moo/HandleMoose.pm'} && !$Moo::sification::disabled) {
140 62         306 Moo::HandleMoose::maybe_reinject_fake_metaclass_for($target);
141             }
142             }
143              
144             sub _non_methods {
145 362     362   10316 my $self = shift;
146 362         766 my ($role) = @_;
147              
148 362         1991 my $non_methods = $self->SUPER::_non_methods(@_);
149              
150 362         6443 my $all_subs = $self->_all_subs($role);
151             $non_methods->{$_} = $all_subs->{$_}
152 362         16419 for _check_tracked($role, [ keys %$all_subs ]);
153              
154 362         1441 return $non_methods;
155             }
156              
157             sub is_role {
158 770     770 1 6853 my ($self, $role) = @_;
159 770         1998 $self->_inhale_if_moose($role);
160 770         12996 $self->SUPER::is_role($role);
161             }
162              
163             sub _inhale_if_moose {
164 770     770   1327 my ($self, $role) = @_;
165 770         1027 my $meta;
166 770 100 100     2206 if (!$self->SUPER::is_role($role)
      100        
      66        
      100        
      100        
      66        
      100        
167             and (
168             $INC{"Moose.pm"}
169             and $meta = Class::MOP::class_of($role)
170             and ref $meta ne 'Moo::HandleMoose::FakeMetaClass'
171             and $meta->isa('Moose::Meta::Role')
172             )
173             or (
174             Mouse::Util->can('find_meta')
175             and $meta = Mouse::Util::find_meta($role)
176             and $meta->isa('Mouse::Meta::Role')
177             )
178             ) {
179 28         958 my $is_mouse = $meta->isa('Mouse::Meta::Role');
180             $INFO{$role}{methods} = {
181 28   100     241 map +($_ => $role->can($_)),
182             grep $role->can($_),
183             grep !($is_mouse && $_ eq 'meta'),
184             grep !$meta->get_method($_)->isa('Class::MOP::Method::Meta'),
185             $meta->get_method_list
186             };
187 28         24162 $APPLIED_TO{$role} = {
188             map +($_->name => 1), $meta->calculate_all_roles
189             };
190 28         1437 $INFO{$role}{requires} = [ $meta->get_required_method_list ];
191             $INFO{$role}{attributes} = [
192 28         1185 map +($_ => do {
193 22         233 my $attr = $meta->get_attribute($_);
194 22 100       142 my $spec = { %{ $is_mouse ? $attr : $attr->original_options } };
  22         499  
195              
196 22 100       203 if ($spec->{isa}) {
197 8         984 require Sub::Quote;
198              
199 8         10214 my $get_constraint = do {
200 8 100       38 my $pkg = $is_mouse
201             ? 'Mouse::Util::TypeConstraints'
202             : 'Moose::Util::TypeConstraints';
203 8         34 _load_module($pkg);
204 8         81 $pkg->can('find_or_create_isa_type_constraint');
205             };
206              
207 8         41 my $tc = $get_constraint->($spec->{isa});
208 8         882 my $check = $tc->_compiled_type_constraint;
209 8         207 my $tc_var = '$_check_for_'.Sub::Quote::sanitize_identifier($tc->name);
210              
211 8         287 $spec->{isa} = Sub::Quote::quote_sub(
212             qq{
213             &${tc_var} or Carp::croak "Type constraint failed for \$_[0]"
214             },
215             { $tc_var => \$check },
216             {
217             package => $role,
218             },
219             );
220              
221 8 100       912 if ($spec->{coerce}) {
222              
223             # Mouse has _compiled_type_coercion straight on the TC object
224 4         10 $spec->{coerce} = $tc->${\(
225 2     2   12 $tc->can('coercion')||sub { $_[0] }
226 4   100     111 )}->_compiled_type_coercion;
227             }
228             }
229 22         229 $spec;
230             }), $meta->get_attribute_list
231             ];
232 28         126 my $mods = $INFO{$role}{modifiers} = [];
233 28         74 foreach my $type (qw(before after around)) {
234             # Mouse pokes its own internals so we have to fall back to doing
235             # the same thing in the absence of the Moose API method
236 84         127 my $map = $meta->${\(
237             $meta->can("get_${type}_method_modifiers_map")
238 24     24   93 or sub { shift->{"${type}_method_modifiers"} }
239 84   100     2420 )};
240 84         596 foreach my $method (keys %$map) {
241 8         29 foreach my $mod (@{$map->{$method}}) {
  8         16  
242 8         27 push @$mods, [ $type => $method => $mod ];
243             }
244             }
245             }
246 28         70 $INFO{$role}{inhaled_from_moose} = 1;
247 28         82 $INFO{$role}{is_role} = 1;
248             }
249             }
250              
251             sub _maybe_make_accessors {
252 220     220   11153 my ($self, $target, $role) = @_;
253 220         368 my $m;
254 220 100 66     2141 if ($INFO{$role} && $INFO{$role}{inhaled_from_moose}
      100        
      100        
      100        
255             or $INC{"Moo.pm"}
256             and $m = Moo->_accessor_maker_for($target)
257             and ref($m) ne 'Method::Generate::Accessor') {
258 42         145 $self->_make_accessors($target, $role);
259             }
260             }
261              
262             sub _make_accessors_if_moose {
263 0     0   0 my ($self, $target, $role) = @_;
264 0 0 0     0 if ($INFO{$role} && $INFO{$role}{inhaled_from_moose}) {
265 0         0 $self->_make_accessors($target, $role);
266             }
267             }
268              
269             sub _make_accessors {
270 42     42   103 my ($self, $target, $role) = @_;
271 42   66     152 my $acc_gen = ($Moo::MAKERS{$target}{accessor} ||= do {
272 6         2250 require Method::Generate::Accessor;
273 6         81 Method::Generate::Accessor->new
274             });
275 42         104 my $con_gen = $Moo::MAKERS{$target}{constructor};
276 42 100       72 my @attrs = @{$INFO{$role}{attributes}||[]};
  42         210  
277 42         190 while (my ($name, $spec) = splice @attrs, 0, 2) {
278             # needed to ensure we got an index for an arrayref based generator
279 36 100       118 if ($con_gen) {
280 32         107 $spec = $con_gen->all_attribute_specs->{$name};
281             }
282 36         189 $acc_gen->generate_method($target, $name, $spec);
283             }
284             }
285              
286             sub _undefer_subs {
287 220     220   4163 my ($self, $target, $role) = @_;
288 220 100       687 if ($INC{'Sub/Defer.pm'}) {
289 198         653 Sub::Defer::undefer_package($role);
290             }
291             }
292              
293             sub role_application_steps {
294 164     164 0 7627 qw(_handle_constructor _undefer_subs _maybe_make_accessors),
295             $_[0]->SUPER::role_application_steps;
296             }
297              
298             sub _build_class_with_roles {
299 42     42   1086 my ($me, $new_name, $superclass, @roles) = @_;
300 42         169 $Moo::MAKERS{$new_name} = {is_class => 1};
301 42         217 $me->SUPER::_build_class_with_roles($new_name, $superclass, @roles);
302              
303 40 100 100     2942 if ($INC{'Moo/HandleMoose.pm'} && !$Moo::sification::disabled) {
304 4         19 Moo::HandleMoose::inject_fake_metaclass_for($new_name);
305             }
306 40         8189 _set_loaded($new_name, (caller)[1]);
307 40         174 return $new_name;
308             }
309              
310             sub _gen_apply_defaults_for {
311 22     22   65 my ($me, $class, @roles) = @_;
312              
313 22 100       40 my @attrs = map @{$INFO{$_}{attributes}||[]}, @roles;
  36         180  
314              
315 22         46 my $con_gen;
316             my $m;
317              
318             return undef
319 22 50 66     207 unless $INC{'Moo.pm'}
      66        
      66        
320             and @attrs
321             and $con_gen = Moo->_constructor_maker_for($class)
322             and $m = Moo->_accessor_maker_for($class);
323              
324 12         45 my $specs = $con_gen->all_attribute_specs;
325              
326 12         35 my %seen;
327             my %captures;
328 12         0 my @set;
329 12         66 while (my ($name, $spec) = splice @attrs, 0, 2) {
330             next
331 18 50       83 if $seen{$name}++;
332              
333             next
334 18 100       85 unless $m->has_eager_default($name, $spec);
335              
336 14         54 my ($has, $has_cap)
337             = $m->generate_simple_has('$_[0]', $name, $spec);
338 14         53 my ($set, $pop_cap)
339             = $m->generate_use_default('$_[0]', $name, $spec, $has);
340              
341 14         48 @captures{keys %$has_cap, keys %$pop_cap}
342             = (values %$has_cap, values %$pop_cap);
343              
344 14         70 push @set, $set;
345             }
346              
347             return undef
348 12 100       48 if !@set;
349              
350 10         76 my $code = join '', map "($_),", @set;
351 126     126   1573 no warnings 'void';
  126         302  
  126         82148  
352 10         74 require Sub::Quote;
353 10         72 return Sub::Quote::quote_sub(
354             "${class}::_apply_defaults",
355             $code,
356             \%captures,
357             {
358             package => $class,
359             no_install => 1,
360             no_defer => 1,
361             }
362             );
363             }
364              
365             sub apply_roles_to_object {
366 24     24 1 7318 my ($me, $object, @roles) = @_;
367 24         142 my $new = $me->SUPER::apply_roles_to_object($object, @roles);
368 24         205 my $class = ref $new;
369 24         110 _set_loaded($class, (caller)[1]);
370              
371 24 100       84 if (!exists $APPLY_DEFAULTS{$class}) {
372 22         76 $APPLY_DEFAULTS{$class} = $me->_gen_apply_defaults_for($class, @roles);
373             }
374 24 100       5809 if (my $apply_defaults = $APPLY_DEFAULTS{$class}) {
375 10         31 local $Carp::Internal{+__PACKAGE__} = 1;
376 10         23 local $Carp::Internal{$class} = 1;
377 10         301 $new->$apply_defaults;
378             }
379 22         752 return $new;
380             }
381              
382             sub _install_single_modifier {
383 38     38   2397 my ($me, @args) = @_;
384 38         100 _install_modifier(@args);
385             }
386              
387             sub _install_does {
388 220     220   24664 my ($me, $to) = @_;
389              
390             # If Role::Tiny actually installed the DOES, give it a name
391 220 100       868 my $new = $me->SUPER::_install_does($to) or return;
392 134         6174 return _name_coderef("${to}::DOES", $new);
393             }
394              
395             sub does_role {
396 24     24 1 8019 my ($proto, $role) = @_;
397 24 100       89 return 1
398             if Role::Tiny::does_role($proto, $role);
399 10         160 my $meta;
400 10 100 66     76 if ($INC{'Moose.pm'}
      100        
      66        
401             and $meta = Class::MOP::class_of($proto)
402             and ref $meta ne 'Moo::HandleMoose::FakeMetaClass'
403             and $meta->can('does_role')
404             ) {
405 4         167 return $meta->does_role($role);
406             }
407 6         53 return 0;
408             }
409              
410             sub _handle_constructor {
411 222     222   2577 my ($me, $to, $role) = @_;
412 222   66     1190 my $attr_info = $INFO{$role} && $INFO{$role}{attributes};
413 222 100 100     1061 return unless $attr_info && @$attr_info;
414 130         259 my $info = $INFO{$to};
415 130   100     806 my $con = $INC{"Moo.pm"} && Moo->_constructor_maker_for($to);
416             my %existing
417 10 100       66 = $info ? @{$info->{attributes} || []}
418 128 100       471 : $con ? %{$con->all_attribute_specs || {}}
  116 100       330  
    100          
419             : ();
420              
421             my @attr_info =
422 128         274 map { @{$attr_info}[$_, $_+1] }
  128         459  
423 138         479 grep { ! $existing{$attr_info->[$_]} }
424 128         614 map { 2 * $_ } 0..@$attr_info/2-1;
  138         397  
425              
426 128 100       494 if ($info) {
    100          
427 10   100     19 push @{$info->{attributes}||=[]}, @attr_info;
  10         75  
428             }
429             elsif ($con) {
430             # shallow copy of the specs since the constructor will assign an index
431 116 100       915 $con->register_attribute_specs(map ref() ? { %$_ } : $_, @attr_info);
432             }
433             }
434              
435             1;
436             __END__
437              
438             =head1 NAME
439              
440             Moo::Role - Minimal Object Orientation support for Roles
441              
442             =head1 SYNOPSIS
443              
444             package My::Role;
445              
446             use Moo::Role;
447             use strictures 2;
448              
449             sub foo { ... }
450              
451             sub bar { ... }
452              
453             has baz => (
454             is => 'ro',
455             );
456              
457             1;
458              
459             And elsewhere:
460              
461             package Some::Class;
462              
463             use Moo;
464             use strictures 2;
465              
466             # bar gets imported, but not foo
467             with 'My::Role';
468              
469             sub foo { ... }
470              
471             1;
472              
473             =head1 DESCRIPTION
474              
475             C<Moo::Role> builds upon L<Role::Tiny>, so look there for most of the
476             documentation on how this works (in particular, using C<Moo::Role> also
477             enables L<strict> and L<warnings>). The main addition here is extra bits to
478             make the roles more "Moosey;" which is to say, it adds L</has>.
479              
480             =head1 IMPORTED SUBROUTINES
481              
482             See L<Role::Tiny/IMPORTED SUBROUTINES> for all the other subroutines that are
483             imported by this module.
484              
485             =head2 has
486              
487             has attr => (
488             is => 'ro',
489             );
490              
491             Declares an attribute for the class to be composed into. See
492             L<Moo/has> for all options.
493              
494             =head1 CLEANING UP IMPORTS
495              
496             L<Moo::Role> cleans up its own imported methods and any imports
497             declared before the C<use Moo::Role> statement automatically.
498             Anything imported after C<use Moo::Role> will be composed into
499             consuming packages. A package that consumes this role:
500              
501             package My::Role::ID;
502              
503             use Digest::MD5 qw(md5_hex);
504             use Moo::Role;
505             use Digest::SHA qw(sha1_hex);
506              
507             requires 'name';
508              
509             sub as_md5 { my ($self) = @_; return md5_hex($self->name); }
510             sub as_sha1 { my ($self) = @_; return sha1_hex($self->name); }
511              
512             1;
513              
514             ..will now have a C<< $self->sha1_hex() >> method available to it
515             that probably does not do what you expect. On the other hand, a call
516             to C<< $self->md5_hex() >> will die with the helpful error message:
517             C<Can't locate object method "md5_hex">.
518              
519             See L<Moo/"CLEANING UP IMPORTS"> for more details.
520              
521             =head1 SUPPORT
522              
523             See L<Moo> for support and contact information.
524              
525             =head1 AUTHORS
526              
527             See L<Moo> for authors.
528              
529             =head1 COPYRIGHT AND LICENSE
530              
531             See L<Moo> for the copyright and license.
532              
533             =cut