Filename | /usr/share/perl5/Role/Tiny.pm |
Statements | Executed 2125 statements in 7.44ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
19 | 2 | 1 | 1.01ms | 2.31ms | _install_methods | Role::Tiny::
32 | 3 | 1 | 795µs | 863µs | _concrete_methods_of | Role::Tiny::
210 | 2 | 1 | 441µs | 441µs | _getglob | Role::Tiny::
4 | 1 | 1 | 348µs | 727µs | _composite_info_for | Role::Tiny::
14 | 1 | 1 | 345µs | 7.59ms | apply_roles_to_package | Role::Tiny::
10 | 1 | 1 | 254µs | 2.48ms | apply_single_role_to_package | Role::Tiny::
19 | 1 | 1 | 235µs | 362µs | _install_does | Role::Tiny::
19 | 2 | 1 | 125µs | 151µs | _check_requires | Role::Tiny::
51 | 2 | 1 | 106µs | 106µs | _getstash | Role::Tiny::
19 | 2 | 1 | 72µs | 118µs | _load_module | Role::Tiny::
19 | 2 | 1 | 61µs | 61µs | _copy_applied_list | Role::Tiny::
19 | 2 | 1 | 58µs | 1.13ms | _install_modifiers | Role::Tiny::
19 | 1 | 1 | 46µs | 46µs | CORE:subst (opcode) | Role::Tiny::
6 | 3 | 2 | 35µs | 44µs | does_role | Role::Tiny::
10 | 1 | 1 | 29µs | 3.40ms | apply_role_to_package | Role::Tiny::
14 | 1 | 1 | 19µs | 19µs | role_application_steps | Role::Tiny::
1 | 1 | 1 | 13µs | 26µs | BEGIN@276 | Role::Tiny::
1 | 1 | 1 | 12µs | 26µs | BEGIN@6 | Role::Tiny::
1 | 1 | 1 | 11µs | 26µs | BEGIN@352 | Role::Tiny::
1 | 1 | 1 | 10µs | 21µs | BEGIN@402 | Role::Tiny::
1 | 1 | 1 | 8µs | 8µs | BEGIN@19 | Role::Tiny::
1 | 1 | 1 | 8µs | 22µs | BEGIN@7 | Role::Tiny::
4 | 1 | 1 | 6µs | 6µs | CORE:sort (opcode) | Role::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:385] | Role::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:401] | Role::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:56] | Role::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:61] | Role::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:65] | Role::Tiny::
0 | 0 | 0 | 0s | 0s | DESTROY | Role::Tiny::__GUARD__::
0 | 0 | 0 | 0s | 0s | _composable_package_for | Role::Tiny::
0 | 0 | 0 | 0s | 0s | _composite_name | Role::Tiny::
0 | 0 | 0 | 0s | 0s | _install_single_modifier | Role::Tiny::
0 | 0 | 0 | 0s | 0s | apply_roles_to_object | Role::Tiny::
0 | 0 | 0 | 0s | 0s | create_class_with_roles | Role::Tiny::
0 | 0 | 0 | 0s | 0s | import | Role::Tiny::
0 | 0 | 0 | 0s | 0s | is_role | Role::Tiny::
0 | 0 | 0 | 0s | 0s | methods_provided_by | Role::Tiny::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Role::Tiny; | ||||
2 | |||||
3 | 210 | 638µs | sub _getglob { \*{$_[0]} } | ||
4 | 51 | 158µs | sub _getstash { \%{"$_[0]::"} } | ||
5 | |||||
6 | 2 | 32µs | 2 | 40µs | # spent 26µs (12+14) within Role::Tiny::BEGIN@6 which was called:
# once (12µs+14µs) by Moo::Role::BEGIN@5 at line 6 # spent 26µs making 1 call to Role::Tiny::BEGIN@6
# spent 14µs making 1 call to strict::import |
7 | 2 | 122µs | 2 | 37µs | # spent 22µs (8+15) within Role::Tiny::BEGIN@7 which was called:
# once (8µs+15µs) by Moo::Role::BEGIN@5 at line 7 # spent 22µs making 1 call to Role::Tiny::BEGIN@7
# spent 15µs making 1 call to warnings::import |
8 | |||||
9 | 1 | 700ns | our $VERSION = '1.003002'; # 1.3.2 | ||
10 | 1 | 19µs | $VERSION = eval $VERSION; # spent 2µs executing statements in string eval | ||
11 | |||||
12 | 1 | 300ns | our %INFO; | ||
13 | 1 | 100ns | our %APPLIED_TO; | ||
14 | 1 | 100ns | our %COMPOSED; | ||
15 | 1 | 100ns | our %COMPOSITE_INFO; | ||
16 | |||||
17 | # Module state workaround totally stolen from Zefram's Module::Runtime. | ||||
18 | |||||
19 | # spent 8µs within Role::Tiny::BEGIN@19 which was called:
# once (8µs+0s) by Moo::Role::BEGIN@5 at line 21 | ||||
20 | 1 | 9µs | *_WORK_AROUND_BROKEN_MODULE_STATE = "$]" < 5.009 ? sub(){1} : sub(){0}; | ||
21 | 1 | 1.76ms | 1 | 8µs | } # spent 8µs making 1 call to Role::Tiny::BEGIN@19 |
22 | |||||
23 | sub Role::Tiny::__GUARD__::DESTROY { | ||||
24 | delete $INC{$_[0]->[0]} if @{$_[0]}; | ||||
25 | } | ||||
26 | |||||
27 | sub _load_module { | ||||
28 | 19 | 89µs | 19 | 46µs | (my $proto = $_[0]) =~ s/::/\//g; # spent 46µs making 19 calls to Role::Tiny::CORE:subst, avg 2µs/call |
29 | 19 | 6µs | $proto .= '.pm'; | ||
30 | 19 | 42µs | return 1 if $INC{$proto}; | ||
31 | # can't just ->can('can') because a sub-package Foo::Bar::Baz | ||||
32 | # creates a 'Baz::' key in Foo::Bar's symbol table | ||||
33 | return 1 if grep !/::$/, keys %{_getstash($_[0])||{}}; | ||||
34 | my $guard = _WORK_AROUND_BROKEN_MODULE_STATE | ||||
35 | && bless([ $proto ], 'Role::Tiny::__GUARD__'); | ||||
36 | require $proto; | ||||
37 | pop @$guard if _WORK_AROUND_BROKEN_MODULE_STATE; | ||||
38 | return 1; | ||||
39 | } | ||||
40 | |||||
41 | sub import { | ||||
42 | my $target = caller; | ||||
43 | my $me = shift; | ||||
44 | strict->import; | ||||
45 | warnings->import(FATAL => 'all'); | ||||
46 | return if $INFO{$target}; # already exported into this package | ||||
47 | $INFO{$target}{is_role} = 1; | ||||
48 | # get symbol table reference | ||||
49 | my $stash = _getstash($target); | ||||
50 | # install before/after/around subs | ||||
51 | foreach my $type (qw(before after around)) { | ||||
52 | *{_getglob "${target}::${type}"} = sub { | ||||
53 | require Class::Method::Modifiers; | ||||
54 | push @{$INFO{$target}{modifiers}||=[]}, [ $type => @_ ]; | ||||
55 | return; | ||||
56 | }; | ||||
57 | } | ||||
58 | *{_getglob "${target}::requires"} = sub { | ||||
59 | push @{$INFO{$target}{requires}||=[]}, @_; | ||||
60 | return; | ||||
61 | }; | ||||
62 | *{_getglob "${target}::with"} = sub { | ||||
63 | $me->apply_roles_to_package($target, @_); | ||||
64 | return; | ||||
65 | }; | ||||
66 | # grab all *non-constant* (stash slot is not a scalarref) subs present | ||||
67 | # in the symbol table and store their refaddrs (no need to forcibly | ||||
68 | # inflate constant subs into real subs) with a map to the coderefs in | ||||
69 | # case of copying or re-use | ||||
70 | my @not_methods = (map { *$_{CODE}||() } grep !ref($_), values %$stash); | ||||
71 | @{$INFO{$target}{not_methods}={}}{@not_methods} = @not_methods; | ||||
72 | # a role does itself | ||||
73 | $APPLIED_TO{$target} = { $target => undef }; | ||||
74 | } | ||||
75 | |||||
76 | # spent 19µs within Role::Tiny::role_application_steps which was called 14 times, avg 1µs/call:
# 14 times (19µs+0s) by Moo::Role::role_application_steps at line 231 of Moo/Role.pm, avg 1µs/call | ||||
77 | 14 | 37µs | qw(_install_methods _check_requires _install_modifiers _copy_applied_list); | ||
78 | } | ||||
79 | |||||
80 | # spent 2.48ms (254µs+2.22) within Role::Tiny::apply_single_role_to_package which was called 10 times, avg 248µs/call:
# 10 times (254µs+2.22ms) by Moo::Role::apply_single_role_to_package at line 248 of Moo/Role.pm, avg 248µs/call | ||||
81 | 10 | 6µs | my ($me, $to, $role) = @_; | ||
82 | |||||
83 | 10 | 8µs | 10 | 64µs | _load_module($role); # spent 64µs making 10 calls to Role::Tiny::_load_module, avg 6µs/call |
84 | |||||
85 | 10 | 2µs | die "This is apply_role_to_package" if ref($to); | ||
86 | 10 | 5µs | die "${role} is not a Role::Tiny" unless $INFO{$role}; | ||
87 | |||||
88 | 10 | 38µs | 10 | 56µs | foreach my $step ($me->role_application_steps) { # spent 56µs making 10 calls to Moo::Role::role_application_steps, avg 6µs/call |
89 | 60 | 123µs | 60 | 2.10ms | $me->$step($to, $role); # spent 1.32ms making 10 calls to Role::Tiny::_install_methods, avg 132µs/call
# spent 512µs making 10 calls to Moo::Role::_handle_constructor, avg 51µs/call
# spent 127µs making 10 calls to Moo::Role::_maybe_make_accessors, avg 13µs/call
# spent 92µs making 10 calls to Role::Tiny::_check_requires, avg 9µs/call
# spent 34µs making 10 calls to Role::Tiny::_copy_applied_list, avg 3µs/call
# spent 22µs making 10 calls to Role::Tiny::_install_modifiers, avg 2µs/call |
90 | } | ||||
91 | } | ||||
92 | |||||
93 | sub _copy_applied_list { | ||||
94 | 19 | 7µs | my ($me, $to, $role) = @_; | ||
95 | # copy our role list into the target's | ||||
96 | 19 | 80µs | @{$APPLIED_TO{$to}||={}}{keys %{$APPLIED_TO{$role}}} = (); | ||
97 | } | ||||
98 | |||||
99 | sub apply_roles_to_object { | ||||
100 | my ($me, $object, @roles) = @_; | ||||
101 | die "No roles supplied!" unless @roles; | ||||
102 | my $class = ref($object); | ||||
103 | bless($object, $me->create_class_with_roles($class, @roles)); | ||||
104 | $object; | ||||
105 | } | ||||
106 | |||||
107 | 1 | 500ns | my $role_suffix = 'A000'; | ||
108 | sub _composite_name { | ||||
109 | my ($me, $superclass, @roles) = @_; | ||||
110 | |||||
111 | my $new_name = join( | ||||
112 | '__WITH__', $superclass, my $compose_name = join '__AND__', @roles | ||||
113 | ); | ||||
114 | |||||
115 | if (length($new_name) > 252) { | ||||
116 | $new_name = $COMPOSED{abbrev}{$new_name} | ||||
117 | ||= substr($new_name, 0, 250 - length $role_suffix).'__'.$role_suffix++; | ||||
118 | } | ||||
119 | return wantarray ? ($new_name, $compose_name) : $new_name; | ||||
120 | } | ||||
121 | |||||
122 | sub create_class_with_roles { | ||||
123 | my ($me, $superclass, @roles) = @_; | ||||
124 | |||||
125 | die "No roles supplied!" unless @roles; | ||||
126 | |||||
127 | _load_module($superclass); | ||||
128 | { | ||||
129 | my %seen; | ||||
130 | $seen{$_}++ for @roles; | ||||
131 | if (my @dupes = grep $seen{$_} > 1, @roles) { | ||||
132 | die "Duplicated roles: ".join(', ', @dupes); | ||||
133 | } | ||||
134 | } | ||||
135 | |||||
136 | my ($new_name, $compose_name) = $me->_composite_name($superclass, @roles); | ||||
137 | |||||
138 | return $new_name if $COMPOSED{class}{$new_name}; | ||||
139 | |||||
140 | foreach my $role (@roles) { | ||||
141 | _load_module($role); | ||||
142 | die "${role} is not a Role::Tiny" unless $INFO{$role}; | ||||
143 | } | ||||
144 | |||||
145 | if ($] >= 5.010) { | ||||
146 | require mro; | ||||
147 | } else { | ||||
148 | require MRO::Compat; | ||||
149 | } | ||||
150 | |||||
151 | my $composite_info = $me->_composite_info_for(@roles); | ||||
152 | my %conflicts = %{$composite_info->{conflicts}}; | ||||
153 | if (keys %conflicts) { | ||||
154 | my $fail = | ||||
155 | join "\n", | ||||
156 | map { | ||||
157 | "Method name conflict for '$_' between roles " | ||||
158 | ."'".join(' and ', sort values %{$conflicts{$_}})."'" | ||||
159 | .", cannot apply these simultaneously to an object." | ||||
160 | } keys %conflicts; | ||||
161 | die $fail; | ||||
162 | } | ||||
163 | |||||
164 | my @composable = map $me->_composable_package_for($_), reverse @roles; | ||||
165 | |||||
166 | # some methods may not exist in the role, but get generated by | ||||
167 | # _composable_package_for (Moose accessors via Moo). filter out anything | ||||
168 | # provided by the composable packages, excluding the subs we generated to | ||||
169 | # make modifiers work. | ||||
170 | my @requires = grep { | ||||
171 | my $method = $_; | ||||
172 | !grep $_->can($method) && !$COMPOSED{role}{$_}{modifiers_only}{$method}, | ||||
173 | @composable | ||||
174 | } @{$composite_info->{requires}}; | ||||
175 | |||||
176 | $me->_check_requires( | ||||
177 | $superclass, $compose_name, \@requires | ||||
178 | ); | ||||
179 | |||||
180 | *{_getglob("${new_name}::ISA")} = [ @composable, $superclass ]; | ||||
181 | |||||
182 | @{$APPLIED_TO{$new_name}||={}}{ | ||||
183 | map keys %{$APPLIED_TO{$_}}, @roles | ||||
184 | } = (); | ||||
185 | |||||
186 | $COMPOSED{class}{$new_name} = 1; | ||||
187 | return $new_name; | ||||
188 | } | ||||
189 | |||||
190 | # preserved for compat, and apply_roles_to_package calls it to allow an | ||||
191 | # updated Role::Tiny to use a non-updated Moo::Role | ||||
192 | |||||
193 | 10 | 28µs | 10 | 3.37ms | # spent 3.40ms (29µs+3.37) within Role::Tiny::apply_role_to_package which was called 10 times, avg 340µs/call:
# 10 times (29µs+3.37ms) by Role::Tiny::apply_roles_to_package at line 198, avg 340µs/call # spent 3.37ms making 10 calls to Moo::Role::apply_single_role_to_package, avg 337µs/call |
194 | |||||
195 | # spent 7.59ms (345µs+7.24) within Role::Tiny::apply_roles_to_package which was called 14 times, avg 542µs/call:
# 14 times (345µs+7.24ms) by Moo::Role::apply_roles_to_package at line 241 of Moo/Role.pm, avg 542µs/call | ||||
196 | 14 | 14µs | my ($me, $to, @roles) = @_; | ||
197 | |||||
198 | 14 | 40µs | 10 | 3.40ms | return $me->apply_role_to_package($to, $roles[0]) if @roles == 1; # spent 3.40ms making 10 calls to Role::Tiny::apply_role_to_package, avg 340µs/call |
199 | |||||
200 | 4 | 15µs | 4 | 727µs | my %conflicts = %{$me->_composite_info_for(@roles)->{conflicts}}; # spent 727µs making 4 calls to Role::Tiny::_composite_info_for, avg 182µs/call |
201 | 4 | 19µs | 4 | 70µs | delete $conflicts{$_} for keys %{ $me->_concrete_methods_of($to) }; # spent 70µs making 4 calls to Role::Tiny::_concrete_methods_of, avg 18µs/call |
202 | 4 | 2µs | if (keys %conflicts) { | ||
203 | my $fail = | ||||
204 | join "\n", | ||||
205 | map { | ||||
206 | "Due to a method name conflict between roles " | ||||
207 | ."'".join(' and ', sort values %{$conflicts{$_}})."'" | ||||
208 | .", the method '$_' must be implemented by '${to}'" | ||||
209 | } keys %conflicts; | ||||
210 | die $fail; | ||||
211 | } | ||||
212 | |||||
213 | # the if guard here is essential since otherwise we accidentally create | ||||
214 | # a $INFO for something that isn't a Role::Tiny (or Moo::Role) because | ||||
215 | # autovivification hates us and wants us to die() | ||||
216 | 4 | 2µs | if ($INFO{$to}) { | ||
217 | delete $INFO{$to}{methods}; # reset since we're about to add methods | ||||
218 | } | ||||
219 | |||||
220 | # backcompat: allow subclasses to use apply_single_role_to_package | ||||
221 | # to apply changes. set a local var so ours does nothing. | ||||
222 | 4 | 500ns | our %BACKCOMPAT_HACK; | ||
223 | 4 | 31µs | 4 | 9µs | if($me ne __PACKAGE__ # spent 9µs making 4 calls to UNIVERSAL::can, avg 2µs/call |
224 | and exists $BACKCOMPAT_HACK{$me} ? $BACKCOMPAT_HACK{$me} : | ||||
225 | $BACKCOMPAT_HACK{$me} = | ||||
226 | $me->can('role_application_steps') | ||||
227 | == \&role_application_steps | ||||
228 | && $me->can('apply_single_role_to_package') | ||||
229 | != \&apply_single_role_to_package | ||||
230 | ) { | ||||
231 | foreach my $role (@roles) { | ||||
232 | $me->apply_single_role_to_package($to, $role); | ||||
233 | } | ||||
234 | } | ||||
235 | else { | ||||
236 | 4 | 7µs | 4 | 24µs | foreach my $step ($me->role_application_steps) { # spent 24µs making 4 calls to Moo::Role::role_application_steps, avg 6µs/call |
237 | 24 | 16µs | foreach my $role (@roles) { | ||
238 | 54 | 117µs | 54 | 3.02ms | $me->$step($to, $role); # spent 1.11ms making 9 calls to Role::Tiny::_install_modifiers, avg 123µs/call
# spent 991µs making 9 calls to Role::Tiny::_install_methods, avg 110µs/call
# spent 772µs making 9 calls to Moo::Role::_handle_constructor, avg 86µs/call
# spent 58µs making 9 calls to Role::Tiny::_check_requires, avg 6µs/call
# spent 56µs making 9 calls to Moo::Role::_maybe_make_accessors, avg 6µs/call
# spent 27µs making 9 calls to Role::Tiny::_copy_applied_list, avg 3µs/call |
239 | } | ||||
240 | } | ||||
241 | } | ||||
242 | 4 | 19µs | $APPLIED_TO{$to}{join('|',@roles)} = 1; | ||
243 | } | ||||
244 | |||||
245 | # spent 727µs (348+380) within Role::Tiny::_composite_info_for which was called 4 times, avg 182µs/call:
# 4 times (348µs+380µs) by Role::Tiny::apply_roles_to_package at line 200, avg 182µs/call | ||||
246 | 4 | 3µs | my ($me, @roles) = @_; | ||
247 | 4 | 38µs | 4 | 6µs | $COMPOSITE_INFO{join('|', sort @roles)} ||= do { # spent 6µs making 4 calls to Role::Tiny::CORE:sort, avg 2µs/call |
248 | 4 | 3µs | foreach my $role (@roles) { | ||
249 | 9 | 12µs | 9 | 54µs | _load_module($role); # spent 54µs making 9 calls to Role::Tiny::_load_module, avg 6µs/call |
250 | } | ||||
251 | 4 | 700ns | my %methods; | ||
252 | 4 | 2µs | foreach my $role (@roles) { | ||
253 | 9 | 25µs | 9 | 319µs | my $this_methods = $me->_concrete_methods_of($role); # spent 319µs making 9 calls to Role::Tiny::_concrete_methods_of, avg 35µs/call |
254 | 9 | 124µs | $methods{$_}{$this_methods->{$_}} = $role for keys %$this_methods; | ||
255 | } | ||||
256 | 4 | 500ns | my %requires; | ||
257 | 4 | 16µs | @requires{map @{$INFO{$_}{requires}||[]}, @roles} = (); | ||
258 | 4 | 29µs | delete $requires{$_} for keys %methods; | ||
259 | 4 | 80µs | delete $methods{$_} for grep keys(%{$methods{$_}}) == 1, keys %methods; | ||
260 | 4 | 9µs | +{ conflicts => \%methods, requires => [keys %requires] } | ||
261 | }; | ||||
262 | } | ||||
263 | |||||
264 | sub _composable_package_for { | ||||
265 | my ($me, $role) = @_; | ||||
266 | my $composed_name = 'Role::Tiny::_COMPOSABLE::'.$role; | ||||
267 | return $composed_name if $COMPOSED{role}{$composed_name}; | ||||
268 | $me->_install_methods($composed_name, $role); | ||||
269 | my $base_name = $composed_name.'::_BASE'; | ||||
270 | # force stash to exist so ->can doesn't complain | ||||
271 | _getstash($base_name); | ||||
272 | # Not using _getglob, since setting @ISA via the typeglob breaks | ||||
273 | # inheritance on 5.10.0 if the stash has previously been accessed an | ||||
274 | # then a method called on the class (in that order!), which | ||||
275 | # ->_install_methods (with the help of ->_install_does) ends up doing. | ||||
276 | 2 | 632µs | 2 | 40µs | # spent 26µs (13+13) within Role::Tiny::BEGIN@276 which was called:
# once (13µs+13µs) by Moo::Role::BEGIN@5 at line 276 # spent 26µs making 1 call to Role::Tiny::BEGIN@276
# spent 13µs making 1 call to strict::unimport |
277 | my $modifiers = $INFO{$role}{modifiers}||[]; | ||||
278 | my @mod_base; | ||||
279 | my @modifiers = grep !$composed_name->can($_), | ||||
280 | do { my %h; @h{map @{$_}[1..$#$_-1], @$modifiers} = (); keys %h }; | ||||
281 | foreach my $modified (@modifiers) { | ||||
282 | push @mod_base, "sub ${modified} { shift->next::method(\@_) }"; | ||||
283 | } | ||||
284 | my $e; | ||||
285 | { | ||||
286 | local $@; | ||||
287 | eval(my $code = join "\n", "package ${base_name};", @mod_base); | ||||
288 | $e = "Evaling failed: $@\nTrying to eval:\n${code}" if $@; | ||||
289 | } | ||||
290 | die $e if $e; | ||||
291 | $me->_install_modifiers($composed_name, $role); | ||||
292 | $COMPOSED{role}{$composed_name} = { | ||||
293 | modifiers_only => { map { $_ => 1 } @modifiers }, | ||||
294 | }; | ||||
295 | return $composed_name; | ||||
296 | } | ||||
297 | |||||
298 | sub _check_requires { | ||||
299 | 19 | 10µs | my ($me, $to, $name, $requires) = @_; | ||
300 | 19 | 55µs | return unless my @requires = @{$requires||$INFO{$name}{requires}||[]}; | ||
301 | 9 | 103µs | 18 | 25µs | if (my @requires_fail = grep !$to->can($_), @requires) { # spent 25µs making 18 calls to UNIVERSAL::can, avg 1µs/call |
302 | # role -> role, add to requires, role -> class, error out | ||||
303 | 1 | 2µs | if (my $to_info = $INFO{$to}) { | ||
304 | push @{$to_info->{requires}||=[]}, @requires_fail; | ||||
305 | } else { | ||||
306 | die "Can't apply ${name} to ${to} - missing ".join(', ', @requires_fail); | ||||
307 | } | ||||
308 | } | ||||
309 | } | ||||
310 | |||||
311 | # spent 863µs (795+69) within Role::Tiny::_concrete_methods_of which was called 32 times, avg 27µs/call:
# 19 times (432µs+42µs) by Role::Tiny::_install_methods at line 339, avg 25µs/call
# 9 times (300µs+19µs) by Role::Tiny::_composite_info_for at line 253, avg 35µs/call
# 4 times (63µs+7µs) by Role::Tiny::apply_roles_to_package at line 201, avg 18µs/call | ||||
312 | 32 | 11µs | my ($me, $role) = @_; | ||
313 | 32 | 13µs | my $info = $INFO{$role}; | ||
314 | # grab role symbol table | ||||
315 | 32 | 29µs | 32 | 69µs | my $stash = _getstash($role); # spent 69µs making 32 calls to Role::Tiny::_getstash, avg 2µs/call |
316 | # reverse so our keys become the values (captured coderefs) in case | ||||
317 | # they got copied or re-used since | ||||
318 | 32 | 165µs | my $not_methods = { reverse %{$info->{not_methods}||{}} }; | ||
319 | $info->{methods} ||= +{ | ||||
320 | # grab all code entries that aren't in the not_methods list | ||||
321 | map { | ||||
322 | 373 | 422µs | my $code = *{$stash->{$_}}{CODE}; | ||
323 | 341 | 162µs | ( ! $code or exists $not_methods->{$code} ) ? () : ($_ => $code) | ||
324 | } grep !ref($stash->{$_}), keys %$stash | ||||
325 | }; | ||||
326 | } | ||||
327 | |||||
328 | sub methods_provided_by { | ||||
329 | my ($me, $role) = @_; | ||||
330 | die "${role} is not a Role::Tiny" unless my $info = $INFO{$role}; | ||||
331 | (keys %{$me->_concrete_methods_of($role)}, @{$info->{requires}||[]}); | ||||
332 | } | ||||
333 | |||||
334 | sub _install_methods { | ||||
335 | 19 | 8µs | my ($me, $to, $role) = @_; | ||
336 | |||||
337 | 19 | 9µs | my $info = $INFO{$role}; | ||
338 | |||||
339 | 19 | 34µs | 19 | 474µs | my $methods = $me->_concrete_methods_of($role); # spent 474µs making 19 calls to Role::Tiny::_concrete_methods_of, avg 25µs/call |
340 | |||||
341 | # grab target symbol table | ||||
342 | 19 | 22µs | 19 | 37µs | my $stash = _getstash($to); # spent 37µs making 19 calls to Role::Tiny::_getstash, avg 2µs/call |
343 | |||||
344 | # determine already extant methods of target | ||||
345 | 19 | 3µs | my %has_methods; | ||
346 | @has_methods{grep | ||||
347 | 19 | 205µs | +(ref($stash->{$_}) || *{$stash->{$_}}{CODE}), | ||
348 | keys %$stash | ||||
349 | } = (); | ||||
350 | |||||
351 | 19 | 61µs | foreach my $i (grep !exists $has_methods{$_}, keys %$methods) { | ||
352 | 2 | 419µs | 2 | 41µs | # spent 26µs (11+15) within Role::Tiny::BEGIN@352 which was called:
# once (11µs+15µs) by Moo::Role::BEGIN@5 at line 352 # spent 26µs making 1 call to Role::Tiny::BEGIN@352
# spent 15µs making 1 call to warnings::unimport |
353 | 202 | 354µs | 202 | 427µs | *{_getglob "${to}::${i}"} = $methods->{$i}; # spent 427µs making 202 calls to Role::Tiny::_getglob, avg 2µs/call |
354 | } | ||||
355 | |||||
356 | 19 | 84µs | 19 | 362µs | $me->_install_does($to); # spent 362µs making 19 calls to Role::Tiny::_install_does, avg 19µs/call |
357 | } | ||||
358 | |||||
359 | sub _install_modifiers { | ||||
360 | 19 | 8µs | my ($me, $to, $name) = @_; | ||
361 | 19 | 47µs | return unless my $modifiers = $INFO{$name}{modifiers}; | ||
362 | 3 | 9µs | if (my $info = $INFO{$to}) { | ||
363 | push @{$info->{modifiers}}, @{$modifiers||[]}; | ||||
364 | } else { | ||||
365 | 2 | 1µs | foreach my $modifier (@{$modifiers||[]}) { | ||
366 | 4 | 11µs | 4 | 1.07ms | $me->_install_single_modifier($to, @$modifier); # spent 1.07ms making 4 calls to Moo::Role::_install_single_modifier, avg 269µs/call |
367 | } | ||||
368 | } | ||||
369 | } | ||||
370 | |||||
371 | 1 | 100ns | my $vcheck_error; | ||
372 | |||||
373 | sub _install_single_modifier { | ||||
374 | my ($me, @args) = @_; | ||||
375 | defined($vcheck_error) or $vcheck_error = do { | ||||
376 | local $@; | ||||
377 | eval { Class::Method::Modifiers->VERSION(1.05); 1 } | ||||
378 | ? 0 | ||||
379 | : $@ | ||||
380 | }; | ||||
381 | $vcheck_error and die $vcheck_error; | ||||
382 | Class::Method::Modifiers::install_modifier(@args); | ||||
383 | } | ||||
384 | |||||
385 | 1 | 2µs | my $FALLBACK = sub { 0 }; | ||
386 | # spent 362µs (235+127) within Role::Tiny::_install_does which was called 19 times, avg 19µs/call:
# 19 times (235µs+127µs) by Role::Tiny::_install_methods at line 356, avg 19µs/call | ||||
387 | 19 | 7µs | my ($me, $to) = @_; | ||
388 | |||||
389 | # only add does() method to classes | ||||
390 | 19 | 13µs | return if $INFO{$to}; | ||
391 | |||||
392 | # add does() only if they don't have one | ||||
393 | 16 | 83µs | 16 | 42µs | *{_getglob "${to}::does"} = \&does_role unless $to->can('does'); # spent 42µs making 16 calls to UNIVERSAL::can, avg 3µs/call |
394 | |||||
395 | 16 | 170µs | 48 | 62µs | return if ($to->can('DOES') and $to->can('DOES') != (UNIVERSAL->can('DOES') || 0)); # spent 62µs making 48 calls to UNIVERSAL::can, avg 1µs/call |
396 | |||||
397 | 8 | 26µs | 8 | 9µs | my $existing = $to->can('DOES') || $to->can('isa') || $FALLBACK; # spent 9µs making 8 calls to UNIVERSAL::can, avg 1µs/call |
398 | my $new_sub = sub { | ||||
399 | my ($proto, $role) = @_; | ||||
400 | Role::Tiny::does_role($proto, $role) or $proto->$existing($role); | ||||
401 | 8 | 22µs | }; | ||
402 | 2 | 311µs | 2 | 31µs | # spent 21µs (10+10) within Role::Tiny::BEGIN@402 which was called:
# once (10µs+10µs) by Moo::Role::BEGIN@5 at line 402 # spent 21µs making 1 call to Role::Tiny::BEGIN@402
# spent 10µs making 1 call to warnings::unimport |
403 | 8 | 30µs | 8 | 14µs | *{_getglob "${to}::DOES"} = $new_sub; # spent 14µs making 8 calls to Role::Tiny::_getglob, avg 2µs/call |
404 | } | ||||
405 | |||||
406 | # spent 44µs (35+9) within Role::Tiny::does_role which was called 6 times, avg 7µs/call:
# 3 times (13µs+3µs) by Search::Elasticsearch::Util::is_compat at line 101 of /opt/flows/lib/lib/perl5/Search/Elasticsearch/Util.pm, avg 6µs/call
# 2 times (12µs+4µs) by Search::Elasticsearch::Util::is_compat at line 96 of /opt/flows/lib/lib/perl5/Search/Elasticsearch/Util.pm, avg 8µs/call
# once (10µs+2µs) by Search::Elasticsearch::Util::is_compat at line 69 of Moo/Object.pm | ||||
407 | 6 | 3µs | my ($proto, $role) = @_; | ||
408 | 6 | 4µs | if ($] >= 5.010) { | ||
409 | require mro; | ||||
410 | } else { | ||||
411 | require MRO::Compat; | ||||
412 | } | ||||
413 | 6 | 26µs | 6 | 9µs | foreach my $class (@{mro::get_linear_isa(ref($proto)||$proto)}) { # spent 9µs making 6 calls to mro::get_linear_isa, avg 1µs/call |
414 | 6 | 20µs | return 1 if exists $APPLIED_TO{$class}{$role}; | ||
415 | } | ||||
416 | return 0; | ||||
417 | } | ||||
418 | |||||
419 | sub is_role { | ||||
420 | my ($me, $role) = @_; | ||||
421 | return !!$INFO{$role}; | ||||
422 | } | ||||
423 | |||||
424 | 1 | 4µs | 1; | ||
425 | |||||
426 | =encoding utf-8 | ||||
427 | |||||
428 | =head1 NAME | ||||
429 | |||||
430 | Role::Tiny - Roles. Like a nouvelle cuisine portion size slice of Moose. | ||||
431 | |||||
432 | =head1 SYNOPSIS | ||||
433 | |||||
434 | package Some::Role; | ||||
435 | |||||
436 | use Role::Tiny; | ||||
437 | |||||
438 | sub foo { ... } | ||||
439 | |||||
440 | sub bar { ... } | ||||
441 | |||||
442 | around baz => sub { ... } | ||||
443 | |||||
444 | 1; | ||||
445 | |||||
446 | else where | ||||
447 | |||||
448 | package Some::Class; | ||||
449 | |||||
450 | use Role::Tiny::With; | ||||
451 | |||||
452 | # bar gets imported, but not foo | ||||
453 | with 'Some::Role'; | ||||
454 | |||||
455 | sub foo { ... } | ||||
456 | |||||
457 | # baz is wrapped in the around modifier by Class::Method::Modifiers | ||||
458 | sub baz { ... } | ||||
459 | |||||
460 | 1; | ||||
461 | |||||
462 | If you wanted attributes as well, look at L<Moo::Role>. | ||||
463 | |||||
464 | =head1 DESCRIPTION | ||||
465 | |||||
466 | C<Role::Tiny> is a minimalist role composition tool. | ||||
467 | |||||
468 | =head1 ROLE COMPOSITION | ||||
469 | |||||
470 | Role composition can be thought of as much more clever and meaningful multiple | ||||
471 | inheritance. The basics of this implementation of roles is: | ||||
472 | |||||
473 | =over 2 | ||||
474 | |||||
475 | =item * | ||||
476 | |||||
477 | If a method is already defined on a class, that method will not be composed in | ||||
478 | from the role. | ||||
479 | |||||
480 | =item * | ||||
481 | |||||
482 | If a method that the role L</requires> to be implemented is not implemented, | ||||
483 | role application will fail loudly. | ||||
484 | |||||
485 | =back | ||||
486 | |||||
487 | Unlike L<Class::C3>, where the B<last> class inherited from "wins," role | ||||
488 | composition is the other way around, where the class wins. If multiple roles | ||||
489 | are applied in a single call (single with statement), then if any of their | ||||
490 | provided methods clash, an exception is raised unless the class provides | ||||
491 | a method since this conflict indicates a potential problem. | ||||
492 | |||||
493 | =head1 IMPORTED SUBROUTINES | ||||
494 | |||||
495 | =head2 requires | ||||
496 | |||||
497 | requires qw(foo bar); | ||||
498 | |||||
499 | Declares a list of methods that must be defined to compose role. | ||||
500 | |||||
501 | =head2 with | ||||
502 | |||||
503 | with 'Some::Role1'; | ||||
504 | |||||
505 | with 'Some::Role1', 'Some::Role2'; | ||||
506 | |||||
507 | Composes another role into the current role (or class via L<Role::Tiny::With>). | ||||
508 | |||||
509 | If you have conflicts and want to resolve them in favour of Some::Role1 you | ||||
510 | can instead write: | ||||
511 | |||||
512 | with 'Some::Role1'; | ||||
513 | with 'Some::Role2'; | ||||
514 | |||||
515 | If you have conflicts and want to resolve different conflicts in favour of | ||||
516 | different roles, please refactor your codebase. | ||||
517 | |||||
518 | =head2 before | ||||
519 | |||||
520 | before foo => sub { ... }; | ||||
521 | |||||
522 | See L<< Class::Method::Modifiers/before method(s) => sub { ... } >> for full | ||||
523 | documentation. | ||||
524 | |||||
525 | Note that since you are not required to use method modifiers, | ||||
526 | L<Class::Method::Modifiers> is lazily loaded and we do not declare it as | ||||
527 | a dependency. If your L<Role::Tiny> role uses modifiers you must depend on | ||||
528 | both L<Class::Method::Modifiers> and L<Role::Tiny>. | ||||
529 | |||||
530 | =head2 around | ||||
531 | |||||
532 | around foo => sub { ... }; | ||||
533 | |||||
534 | See L<< Class::Method::Modifiers/around method(s) => sub { ... } >> for full | ||||
535 | documentation. | ||||
536 | |||||
537 | Note that since you are not required to use method modifiers, | ||||
538 | L<Class::Method::Modifiers> is lazily loaded and we do not declare it as | ||||
539 | a dependency. If your L<Role::Tiny> role uses modifiers you must depend on | ||||
540 | both L<Class::Method::Modifiers> and L<Role::Tiny>. | ||||
541 | |||||
542 | =head2 after | ||||
543 | |||||
544 | after foo => sub { ... }; | ||||
545 | |||||
546 | See L<< Class::Method::Modifiers/after method(s) => sub { ... } >> for full | ||||
547 | documentation. | ||||
548 | |||||
549 | Note that since you are not required to use method modifiers, | ||||
550 | L<Class::Method::Modifiers> is lazily loaded and we do not declare it as | ||||
551 | a dependency. If your L<Role::Tiny> role uses modifiers you must depend on | ||||
552 | both L<Class::Method::Modifiers> and L<Role::Tiny>. | ||||
553 | |||||
554 | =head1 SUBROUTINES | ||||
555 | |||||
556 | =head2 does_role | ||||
557 | |||||
558 | if (Role::Tiny::does_role($foo, 'Some::Role')) { | ||||
559 | ... | ||||
560 | } | ||||
561 | |||||
562 | Returns true if class has been composed with role. | ||||
563 | |||||
564 | This subroutine is also installed as ->does on any class a Role::Tiny is | ||||
565 | composed into unless that class already has an ->does method, so | ||||
566 | |||||
567 | if ($foo->does('Some::Role')) { | ||||
568 | ... | ||||
569 | } | ||||
570 | |||||
571 | will work for classes but to test a role, one must use ::does_role directly. | ||||
572 | |||||
573 | Additionally, Role::Tiny will override the standard Perl C<DOES> method | ||||
574 | for your class. However, if C<any> class in your class' inheritance | ||||
575 | hierarchy provides C<DOES>, then Role::Tiny will not override it. | ||||
576 | |||||
577 | =head1 METHODS | ||||
578 | |||||
579 | =head2 apply_roles_to_package | ||||
580 | |||||
581 | Role::Tiny->apply_roles_to_package( | ||||
582 | 'Some::Package', 'Some::Role', 'Some::Other::Role' | ||||
583 | ); | ||||
584 | |||||
585 | Composes role with package. See also L<Role::Tiny::With>. | ||||
586 | |||||
587 | =head2 apply_roles_to_object | ||||
588 | |||||
589 | Role::Tiny->apply_roles_to_object($foo, qw(Some::Role1 Some::Role2)); | ||||
590 | |||||
591 | Composes roles in order into object directly. Object is reblessed into the | ||||
592 | resulting class. | ||||
593 | |||||
594 | =head2 create_class_with_roles | ||||
595 | |||||
596 | Role::Tiny->create_class_with_roles('Some::Base', qw(Some::Role1 Some::Role2)); | ||||
597 | |||||
598 | Creates a new class based on base, with the roles composed into it in order. | ||||
599 | New class is returned. | ||||
600 | |||||
601 | =head2 is_role | ||||
602 | |||||
603 | Role::Tiny->is_role('Some::Role1') | ||||
604 | |||||
605 | Returns true if the given package is a role. | ||||
606 | |||||
607 | =head1 SEE ALSO | ||||
608 | |||||
609 | L<Role::Tiny> is the attribute-less subset of L<Moo::Role>; L<Moo::Role> is | ||||
610 | a meta-protocol-less subset of the king of role systems, L<Moose::Role>. | ||||
611 | |||||
612 | If you don't want method modifiers and do want to be forcibly restricted | ||||
613 | to a single role application per class, Ovid's L<Role::Basic> exists. But | ||||
614 | Stevan Little (the L<Moose> author) and I don't find the additional | ||||
615 | restrictions to be amazingly helpful in most cases; L<Role::Basic>'s choices | ||||
616 | are more a guide to what you should prefer doing, to our mind, rather than | ||||
617 | something that needs to be enforced. | ||||
618 | |||||
619 | =head1 AUTHOR | ||||
620 | |||||
621 | mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk> | ||||
622 | |||||
623 | =head1 CONTRIBUTORS | ||||
624 | |||||
625 | dg - David Leadbeater (cpan:DGL) <dgl@dgl.cx> | ||||
626 | |||||
627 | frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com> | ||||
628 | |||||
629 | hobbs - Andrew Rodland (cpan:ARODLAND) <arodland@cpan.org> | ||||
630 | |||||
631 | jnap - John Napiorkowski (cpan:JJNAPIORK) <jjn1056@yahoo.com> | ||||
632 | |||||
633 | ribasushi - Peter Rabbitson (cpan:RIBASUSHI) <ribasushi@cpan.org> | ||||
634 | |||||
635 | chip - Chip Salzenberg (cpan:CHIPS) <chip@pobox.com> | ||||
636 | |||||
637 | ajgb - Alex J. G. Burzyński (cpan:AJGB) <ajgb@cpan.org> | ||||
638 | |||||
639 | doy - Jesse Luehrs (cpan:DOY) <doy at tozt dot net> | ||||
640 | |||||
641 | perigrin - Chris Prather (cpan:PERIGRIN) <chris@prather.org> | ||||
642 | |||||
643 | Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com> | ||||
644 | |||||
645 | ilmari - Dagfinn Ilmari Mannsåker (cpan:ILMARI) <ilmari@ilmari.org> | ||||
646 | |||||
647 | tobyink - Toby Inkster (cpan:TOBYINK) <tobyink@cpan.org> | ||||
648 | |||||
649 | =head1 COPYRIGHT | ||||
650 | |||||
651 | Copyright (c) 2010-2012 the Role::Tiny L</AUTHOR> and L</CONTRIBUTORS> | ||||
652 | as listed above. | ||||
653 | |||||
654 | =head1 LICENSE | ||||
655 | |||||
656 | This library is free software and may be distributed under the same terms | ||||
657 | as perl itself. | ||||
658 | |||||
659 | =cut | ||||
# spent 6µs within Role::Tiny::CORE:sort which was called 4 times, avg 2µs/call:
# 4 times (6µs+0s) by Role::Tiny::_composite_info_for at line 247, avg 2µs/call | |||||
# spent 46µs within Role::Tiny::CORE:subst which was called 19 times, avg 2µs/call:
# 19 times (46µs+0s) by Role::Tiny::_load_module at line 28, avg 2µs/call |