File Coverage

blib/lib/Jojo/Role.pm
Criterion Covered Total %
statement 67 67 100.0
branch 4 6 66.6
condition 3 6 50.0
subroutine 18 18 100.0
pod 2 3 66.6
total 94 100 94.0


line stmt bran cond sub pod time code
1              
2             package Jojo::Role;
3              
4             # ABSTRACT: Role::Tiny + lexical "with"
5 17     17   998377 use 5.018;
  17         193  
6 17     17   90 use strict;
  17         31  
  17         355  
7 17     17   76 use warnings;
  17         28  
  17         466  
8 17     17   10336 use utf8;
  17         287  
  17         93  
9 17     17   518 use feature ();
  17         37  
  17         261  
10 17     17   6895 use experimental ();
  17         54361  
  17         1315  
11              
12             our $VERSION = '0.6.0';
13              
14             BEGIN {
15 17     17   8239 require Jojo::Role::Tiny;
16 17         356 Jojo::Role::Tiny->VERSION('2.000006');
17 17         881 our @ISA = qw(Jojo::Role::Tiny);
18             }
19              
20 17     17   8056 use Sub::Inject 0.3.0 ();
  17         7710  
  17         847  
21              
22             # Aliasing of Jojo::Role::Tiny symbols
23             BEGIN {
24 17     17   65 *INFO = \%Jojo::Role::Tiny::INFO;
25 17         10754 *ON_ROLE_CREATE = \@Jojo::Role::Tiny::ON_ROLE_CREATE;
26             }
27              
28             our %INFO;
29              
30             our %EXPORT_TAGS;
31             our %EXPORT_GEN;
32              
33             # Jojo::Role->apply_roles_to_package('Some::Package', qw(Some::Role +Other::Role));
34             sub apply_roles_to_package {
35 34     34 1 1458 my ($self, $target) = (shift, shift);
36             return $self->Jojo::Role::Tiny::apply_roles_to_package($target,
37 34 50       76 map { /^\+(.+)$/ ? "${target}::Role::$1" : $_ } @_);
  44         272  
38             }
39              
40             # Jojo::Role->create_class_with_roles('Some::Base', qw(Some::Role1 +Role2));
41             sub create_class_with_roles {
42 41     41 1 473 my ($self, $target) = (shift, shift);
43             return $self->Jojo::Role::Tiny::create_class_with_roles($target,
44 41 50       83 map { /^\+(.+)$/ ? "${target}::Role::$1" : $_ } @_);
  46         223  
45             }
46              
47             sub import {
48 74     74   13492 my $target = caller;
49 74         143 my $me = shift;
50              
51             # Jojo modules are strict!
52 74         1237 $_->import for qw(strict warnings utf8);
53 74         5807 feature->import(':5.18');
54 74         425 experimental->import('lexical_subs');
55              
56 74         2141 my $flag = shift;
57 74 100       227 if (!$flag) {
58 49         272 $me->make_role($target);
59 49         93 $flag = '-role';
60             }
61              
62 74   50     102 my @exports = @{$EXPORT_TAGS{$flag} // []};
  74         271  
63 74         202 @_ = $me->_generate_subs($target, @exports);
64 74         362 goto &Sub::Inject::sub_inject;
65             }
66              
67 74     74 0 187 sub role_provider { $_[0] }
68              
69             BEGIN {
70 17     17   145 %EXPORT_TAGS = ( #
71             -role => [qw(after around before requires with)],
72             -with => [qw(with)],
73             );
74              
75             %EXPORT_GEN = (
76             requires => sub {
77 49         103 my (undef, $target) = @_;
78             return sub {
79 11   50 8   4819 push @{$INFO{$target}{requires} ||= []}, @_;
  11         87  
80 11         195 return;
81 49         181 };
82             },
83             with => sub {
84 74         181 my ($me, $target) = (shift->role_provider, shift);
85             return sub {
86 33     27   6605 $me->apply_roles_to_package($target, @_);
87 27         1834 return;
88 74         495 };
89             },
90 17         125 );
91              
92             # before/after/around
93 17         53 foreach my $type (qw(before after around)) {
94             $EXPORT_GEN{$type} = sub {
95 147         249 my (undef, $target) = @_;
96             return sub {
97 2   50 2   2817 push @{$INFO{$target}{modifiers} ||= []}, [$type => @_];
  2         16  
98 2         5 return;
99 147         781 };
100 51         2344 };
101             }
102             }
103              
104             sub _generate_subs {
105 74     74   175 my ($class, $target) = (shift, shift);
106 74         136 return map { my $cb = $EXPORT_GEN{$_}; $_ => $class->$cb($target) } @_;
  270         458  
  270         503  
107             }
108              
109             1;
110              
111             #pod =encoding utf8
112             #pod
113             #pod =head1 SYNOPSIS
114             #pod
115             #pod package Some::Role {
116             #pod use Jojo::Role; # requires perl 5.18+
117             #pod
118             #pod sub foo {...}
119             #pod sub bar {...}
120             #pod around baz => sub {...};
121             #pod }
122             #pod
123             #pod package Some::Class {
124             #pod use Jojo::Role -with;
125             #pod with 'Some::Role';
126             #pod
127             #pod # bar gets imported, but not foo
128             #pod sub foo {...}
129             #pod
130             #pod # baz is wrapped in the around modifier by Class::Method::Modifiers
131             #pod sub baz {...}
132             #pod }
133             #pod
134             #pod =head1 DESCRIPTION
135             #pod
136             #pod L works kind of like L but C, C,
137             #pod C, C and C are exported
138             #pod as lexical subroutines.
139             #pod
140             #pod This is a companion to L.
141             #pod
142             #pod L may be used in two ways. First, to declare a role, which is done
143             #pod with
144             #pod
145             #pod use Jojo::Base;
146             #pod use Jojo::Base -role; # Longer version
147             #pod
148             #pod Second, to compose one or more roles into a class, via
149             #pod
150             #pod use Jojo::Base -with;
151             #pod
152             #pod =head1 IMPORTED -role SUBROUTINES
153             #pod
154             #pod The C<-role> tag exports the following subroutines into the caller.
155             #pod
156             #pod =head2 after
157             #pod
158             #pod after foo => sub { ... };
159             #pod
160             #pod Declares an
161             #pod L<< "after" |Class::Method::Modifiers/after method(s) => sub { ... } >>
162             #pod modifier to be applied to the named method at composition time.
163             #pod
164             #pod =head2 around
165             #pod
166             #pod around => sub { ... };
167             #pod
168             #pod Declares an
169             #pod L<< "around" |Class::Method::Modifiers/around method(s) => sub { ... } >>
170             #pod modifier to be applied to the named method at composition time.
171             #pod
172             #pod =head2 before
173             #pod
174             #pod before => sub { ... };
175             #pod
176             #pod Declares a
177             #pod L<< "before" |Class::Method::Modifiers/before method(s) => sub { ... } >>
178             #pod modifier to be applied to the named method at composition time.
179             #pod
180             #pod =head2 requires
181             #pod
182             #pod requires qw(foo bar);
183             #pod
184             #pod Declares a list of methods that must be defined to compose the role.
185             #pod
186             #pod =head2 with
187             #pod
188             #pod with 'Some::Role';
189             #pod
190             #pod with 'Some::Role1', 'Some::Role2';
191             #pod
192             #pod Composes one or more roles into the current role.
193             #pod
194             #pod =head1 IMPORTED -with SUBROUTINES
195             #pod
196             #pod The C<-with> tag exports the following subroutine into the caller.
197             #pod It is equivalent to using L.
198             #pod
199             #pod =head2 with
200             #pod
201             #pod with 'Some::Role1', 'Some::Role2';
202             #pod
203             #pod Composes one or more roles into the current class.
204             #pod
205             #pod =head1 METHODS
206             #pod
207             #pod L inherits all methods from L and implements the
208             #pod following new ones.
209             #pod
210             #pod =head2 apply_roles_to_package
211             #pod
212             #pod Jojo::Role->apply_roles_to_package('Some::Package', qw(Some::Role +Other::Role));
213             #pod
214             #pod =head2 create_class_with_roles
215             #pod
216             #pod Jojo::Role->create_class_with_roles('Some::Base', qw(Some::Role1 +Role2));
217             #pod
218             #pod =head2 import
219             #pod
220             #pod Jojo::Role->import();
221             #pod Jojo::Role->import(-role);
222             #pod Jojo::Role->import(-with);
223             #pod
224             #pod =head2 make_role
225             #pod
226             #pod Jojo::Role->make_role('Some::Package');
227             #pod
228             #pod Promotes a given package to a role.
229             #pod No subroutines are imported into C<'Some::Package'>.
230             #pod
231             #pod =head1 CAVEATS
232             #pod
233             #pod =over 4
234             #pod
235             #pod =item *
236             #pod
237             #pod L requires perl 5.18 or newer
238             #pod
239             #pod =item *
240             #pod
241             #pod Because a lexical sub does not behave like a package import,
242             #pod some code may need to be enclosed in blocks to avoid warnings like
243             #pod
244             #pod "state" subroutine &with masks earlier declaration in same scope at...
245             #pod
246             #pod =back
247             #pod
248             #pod =head1 SEE ALSO
249             #pod
250             #pod L, L.
251             #pod
252             #pod =head1 ACKNOWLEDGMENTS
253             #pod
254             #pod Thanks to the authors of L, which hold
255             #pod the copyright over the original code.
256             #pod
257             #pod =cut
258              
259             __END__