File Coverage

blib/lib/Jojo/Base.pm
Criterion Covered Total %
statement 83 85 97.6
branch 18 18 100.0
condition 6 9 66.6
subroutine 19 19 100.0
pod 1 2 50.0
total 127 133 95.4


line stmt bran cond sub pod time code
1              
2             package Jojo::Base;
3             $Jojo::Base::VERSION = '0.7.0';
4             # ABSTRACT: Mojo::Base + lexical "has"
5 5     5   2064 use 5.018;
  5         33  
6 5     5   22 use strict;
  5         20  
  5         95  
7 5     5   20 use warnings;
  5         7  
  5         114  
8 5     5   2563 use utf8;
  5         59  
  5         22  
9 5     5   332 use feature ();
  5         8  
  5         56  
10 5     5   1984 use experimental ();
  5         14656  
  5         212  
11              
12             BEGIN {
13 5     5   1916 require Mojo::Base;
14 5         589996 our @ISA = qw(Mojo::Base);
15             }
16              
17 5     5   54 use Carp ();
  5         8  
  5         84  
18 5     5   20 use Mojo::Util ();
  5         7  
  5         96  
19 5     5   2161 use Sub::Inject 0.2.0 ();
  5         1911  
  5         237  
20              
21             use constant ROLES =>
22 5     5   30 !!(eval { require Jojo::Role; Jojo::Role->VERSION('0.5.0'); 1 });
  5         11  
  5         6  
  5         1883  
  5         5115  
  5         360  
23              
24 5     5   31 use constant SIGNATURES => ($] >= 5.020);
  5         8  
  5         1039  
25              
26             our %EXPORT_TAGS;
27             our %EXPORT_GEN;
28              
29             sub import {
30 21     21   224185 my $class = shift;
31 21 100       1980 return unless my $flag = shift;
32 17         33 my $caller = caller;
33              
34             # Base
35 17         22 my $base;
36 17 100 50     124 if ($flag eq '-base') { $base = $class }
  5 100 66     10  
    100          
    100          
37              
38             # Strict
39             elsif ($flag eq '-strict') { }
40              
41             # Role
42             elsif ($flag eq '-role') {
43 2         3 Carp::croak 'Jojo::Role 0.5.0+ is required for roles' unless ROLES;
44 2         5 Jojo::Role->make_role($caller);
45             }
46              
47             # Module
48             elsif (($base = $flag) && ($flag = '-base') && !$base->can('new')) {
49 1         7 require(Mojo::Util::class_to_path($base));
50             }
51              
52             # Jojo modules are strict!
53 17         319 $_->import for qw(strict warnings utf8);
54 17         783 feature->import(':5.18');
55 17         85 experimental->import('lexical_subs');
56              
57             # Signatures (Perl 5.20+)
58 17 100 100     534 if ((shift || '') eq '-signatures') {
59 1         2 Carp::croak 'Subroutine signatures require Perl 5.20+' unless SIGNATURES;
60 1         5 experimental->import('signatures');
61             }
62              
63             # ISA
64 17 100       63 if ($base) {
65 5     5   29 no strict 'refs';
  5         10  
  5         1509  
66 9         14 push @{"${caller}::ISA"}, $base;
  9         99  
67             }
68              
69 17   50     28 my @exports = @{$EXPORT_TAGS{$flag} // []};
  17         61  
70 17 100       1724 if (@exports) {
71 11         34 @_ = $class->_generate_subs($caller, @exports);
72 11         125 goto &Sub::Inject::sub_inject;
73             }
74             }
75              
76 11     11 0 52 sub role_provider {'Jojo::Role'}
77              
78             sub with_roles {
79 6     6 1 8503 Carp::croak 'Jojo::Role 0.5.0+ is required for roles' unless ROLES;
80 6         15 my ($self, @roles) = @_;
81              
82 6 100       34 return Jojo::Role->create_class_with_roles($self, @roles)
83             unless my $class = Scalar::Util::blessed $self;
84              
85 1         9 return Jojo::Role->apply_roles_to_object($self, @roles);
86             }
87              
88             BEGIN {
89 5     5   44 %EXPORT_TAGS = (-base => [qw(has with)], -role => [qw(has)], -strict => [],);
90              
91             %EXPORT_GEN = (
92             has => sub {
93 11         20 my (undef, $target) = @_;
94 10     10   580 return sub { Mojo::Base::attr($target, @_) }
95 11         56 },
96             with => sub { # dummy
97 0         0 return sub { Carp::croak 'Jojo::Role 0.5.0+ is required for roles' }
98 0         0 },
99 5         26 );
100              
101 5         8 return unless ROLES;
102              
103 5         8 push @{$EXPORT_TAGS{-role}}, @{$Jojo::Role::EXPORT_TAGS{-role}};
  5         10  
  5         16  
104              
105             $EXPORT_GEN{$_} = $Jojo::Role::EXPORT_GEN{$_}
106 5         9 for @{$Jojo::Role::EXPORT_TAGS{-role}};
  5         620  
107             }
108              
109             sub _generate_subs {
110 11     11   22 my ($class, $target) = (shift, shift);
111 11         21 return map { my $cb = $EXPORT_GEN{$_}; $_ => $class->$cb($target) } @_;
  30         92  
  30         62  
112             }
113              
114             1;
115              
116             #pod =encoding utf8
117             #pod
118             #pod =head1 SYNOPSIS
119             #pod
120             #pod package Cat {
121             #pod use Jojo::Base -base; # requires perl 5.18+
122             #pod
123             #pod has name => 'Nyan';
124             #pod has ['age', 'weight'] => 4;
125             #pod }
126             #pod
127             #pod package Tiger {
128             #pod use Jojo::Base 'Cat';
129             #pod
130             #pod has friend => sub { Cat->new };
131             #pod has stripes => 42;
132             #pod }
133             #pod
134             #pod package main;
135             #pod use Jojo::Base -strict;
136             #pod
137             #pod my $mew = Cat->new(name => 'Longcat');
138             #pod say $mew->age;
139             #pod say $mew->age(3)->weight(5)->age;
140             #pod
141             #pod my $rawr = Tiger->new(stripes => 38, weight => 250);
142             #pod say $rawr->tap(sub { $_->friend->name('Tacgnol') })->weight;
143             #pod
144             #pod =head1 DESCRIPTION
145             #pod
146             #pod L works kind of like L but C is imported
147             #pod as lexical subroutine.
148             #pod
149             #pod L, like L, is a simple base class designed
150             #pod to be effortless and powerful.
151             #pod
152             #pod # Enables "strict", "warnings", "utf8" and Perl 5.18 and "lexical_subs" features
153             #pod use Jojo::Base -strict;
154             #pod use Jojo::Base -base;
155             #pod use Jojo::Base 'SomeBaseClass';
156             #pod use Jojo::Base -role;
157             #pod
158             #pod All four forms save a lot of typing. Note that role support depends on
159             #pod L (0.5.0+).
160             #pod
161             #pod # use Jojo::Base -strict;
162             #pod use strict;
163             #pod use warnings;
164             #pod use utf8;
165             #pod use feature ':5.18';
166             #pod use experimental 'lexical_subs';
167             #pod use IO::Handle ();
168             #pod
169             #pod # use Jojo::Base -base;
170             #pod use strict;
171             #pod use warnings;
172             #pod use utf8;
173             #pod use feature ':5.18';
174             #pod use experimental 'lexical_subs';
175             #pod use IO::Handle ();
176             #pod push @ISA, 'Jojo::Base';
177             #pod state sub has { ... } # attributes
178             #pod state sub with { ... } # role composition
179             #pod
180             #pod # use Jojo::Base 'SomeBaseClass';
181             #pod use strict;
182             #pod use warnings;
183             #pod use utf8;
184             #pod use feature ':5.18';
185             #pod use experimental 'lexical_subs';
186             #pod use IO::Handle ();
187             #pod require SomeBaseClass;
188             #pod push @ISA, 'SomeBaseClass';
189             #pod state sub has { ... } # attributes
190             #pod state sub with { ... } # role composition
191             #pod
192             #pod # use Jojo::Base -role;
193             #pod use strict;
194             #pod use warnings;
195             #pod use utf8;
196             #pod use feature ':5.18';
197             #pod use experimental 'lexical_subs';
198             #pod use IO::Handle ();
199             #pod use Jojo::Role;
200             #pod state sub has { ... } # attributes
201             #pod
202             #pod On Perl 5.20+ you can also append a C<-signatures> flag to all four forms and
203             #pod enable support for L.
204             #pod
205             #pod # Also enable signatures
206             #pod use Jojo::Base -strict, -signatures;
207             #pod use Jojo::Base -base, -signatures;
208             #pod use Jojo::Base 'SomeBaseClass', -signatures;
209             #pod use Jojo::Base -role, -signatures;
210             #pod
211             #pod This will also disable experimental warnings on versions of Perl where this
212             #pod feature was still experimental.
213             #pod
214             #pod =head2 DIFFERENCES FROM C
215             #pod
216             #pod =over 4
217             #pod
218             #pod =item *
219             #pod
220             #pod All functions are exported as lexical subs
221             #pod
222             #pod =item *
223             #pod
224             #pod Role support depends on L instead of L
225             #pod
226             #pod =item *
227             #pod
228             #pod C is exported alongside C (when L is available)
229             #pod
230             #pod =item *
231             #pod
232             #pod Feature bundle for Perl 5.18 is enabled by default, instead of 5.10
233             #pod
234             #pod =item *
235             #pod
236             #pod Support for L is enabled
237             #pod by default
238             #pod
239             #pod =back
240             #pod
241             #pod =head1 FUNCTIONS
242             #pod
243             #pod L implements the following functions, which can be imported with
244             #pod the C<-base> flag, or by setting a base class.
245             #pod
246             #pod =head2 has
247             #pod
248             #pod has 'name';
249             #pod has ['name1', 'name2', 'name3'];
250             #pod has name => 'foo';
251             #pod has name => sub {...};
252             #pod has ['name1', 'name2', 'name3'] => 'foo';
253             #pod has ['name1', 'name2', 'name3'] => sub {...};
254             #pod
255             #pod Create attributes for hash-based objects, just like the L method.
256             #pod
257             #pod =head2 with
258             #pod
259             #pod with 'SubClass::Role::One';
260             #pod with '+One', '+Two';
261             #pod
262             #pod Composes the current package with one or more L roles.
263             #pod For roles following the naming scheme C you
264             #pod can use the shorthand C<+RoleName>. Note that role support depends on
265             #pod L (0.5.0+).
266             #pod
267             #pod It works with L or L roles.
268             #pod
269             #pod =head1 METHODS
270             #pod
271             #pod L inherits all methods from L and implements
272             #pod the following new ones.
273             #pod
274             #pod =head2 with_roles
275             #pod
276             #pod my $new_class = SubClass->with_roles('SubClass::Role::One');
277             #pod my $new_class = SubClass->with_roles('+One', '+Two');
278             #pod $object = $object->with_roles('+One', '+Two');
279             #pod
280             #pod Create a new class with one or more roles. If called on a class
281             #pod returns the new class, or if called on an object reblesses the object into the
282             #pod new class. For roles following the naming scheme C you
283             #pod can use the shorthand C<+RoleName>. Note that role support depends on
284             #pod L (0.5.0+).
285             #pod
286             #pod # Create a new class with the role "SubClass::Role::Foo" and instantiate it
287             #pod my $new_class = SubClass->with_roles('+Foo');
288             #pod my $object = $new_class->new;
289             #pod
290             #pod It works with L or L roles.
291             #pod
292             #pod =head1 CAVEATS
293             #pod
294             #pod =over 4
295             #pod
296             #pod =item *
297             #pod
298             #pod L requires perl 5.18 or newer
299             #pod
300             #pod =item *
301             #pod
302             #pod Because a lexical sub does not behave like a package import,
303             #pod some code may need to be enclosed in blocks to avoid warnings like
304             #pod
305             #pod "state" subroutine &has masks earlier declaration in same scope at...
306             #pod
307             #pod =back
308             #pod
309             #pod =head1 SEE ALSO
310             #pod
311             #pod L, L.
312             #pod
313             #pod =head1 ACKNOWLEDGMENTS
314             #pod
315             #pod Thanks to Sebastian Riedel and others, the authors
316             #pod and copyright holders of L.
317             #pod
318             #pod =cut
319              
320             __END__