File Coverage

blib/lib/Jojo/Base.pm
Criterion Covered Total %
statement 81 84 96.4
branch 18 18 100.0
condition 6 9 66.6
subroutine 18 19 94.7
pod 1 2 50.0
total 124 132 93.9


line stmt bran cond sub pod time code
1              
2             package Jojo::Base;
3             $Jojo::Base::VERSION = '0.5.0';
4             # ABSTRACT: Mojo::Base + lexical "has"
5 5     5   1843 use 5.018;
  5         37  
6 5     5   25 use strict;
  5         9  
  5         101  
7 5     5   23 use warnings;
  5         8  
  5         142  
8 5     5   2319 use utf8;
  5         62  
  5         25  
9 5     5   153 use feature ();
  5         10  
  5         78  
10 5     5   1413 use experimental ();
  5         16317  
  5         305  
11              
12             BEGIN {
13 5     5   1493 require Mojo::Base;
14 5         675371 our @ISA = qw(Mojo::Base);
15             }
16              
17 5     5   68 use Carp ();
  5         12  
  5         103  
18 5     5   31 use Mojo::Util ();
  5         12  
  5         142  
19 5     5   2183 use Sub::Inject 0.2.0 ();
  5         2446  
  5         303  
20              
21             use constant ROLES =>
22 5     5   35 !!(eval { require Jojo::Role; Jojo::Role->VERSION('0.4.0'); 1 });
  5         11  
  5         10  
  5         1378  
  5         6693  
  5         517  
23              
24 5     5   46 use constant SIGNATURES => ($] >= 5.020);
  5         14  
  5         1109  
25              
26             our %EXPORT_TAGS;
27             our %EXPORT_GEN;
28              
29             sub import {
30 21     21   222569 my ($class, $caller) = (shift, caller);
31 21 100       1937 return unless my $flag = shift;
32              
33             # Base
34 17         31 my $base;
35 17 100 50     138 if ($flag eq '-base') { $base = $class }
  5 100 66     55  
    100          
    100          
36              
37             # Strict
38             elsif ($flag eq '-strict') { }
39              
40             # Role
41             elsif ($flag eq '-role') {
42 2         20 Carp::croak 'Jojo::Role 0.4.0+ is required for roles' unless ROLES;
43 2         7 Jojo::Role->_become_role($caller);
44             }
45              
46             # Module
47             elsif (($base = $flag) && ($flag = '-base') && !$base->can('new')) {
48 1         24 require(Mojo::Util::class_to_path($base));
49             }
50              
51             # Jojo modules are strict!
52 17         379 $_->import for qw(strict warnings utf8);
53 17         1004 feature->import(':5.18');
54 17         109 experimental->import('lexical_subs');
55              
56             # Signatures (Perl 5.20+)
57 17 100 100     614 if ((shift || '') eq '-signatures') {
58 1         2 Carp::croak 'Subroutine signatures require Perl 5.20+' unless SIGNATURES;
59 1         3 experimental->import('signatures');
60             }
61              
62             # ISA
63 17 100       62 if ($base) {
64 5     5   34 no strict 'refs';
  5         12  
  5         1843  
65 9         24 push @{"${caller}::ISA"}, $base;
  9         122  
66             }
67              
68 17   50     49 my @exports = @{$EXPORT_TAGS{$flag} // []};
  17         81  
69 17 100       2072 if (@exports) {
70 11         38 @_ = $class->_generate_subs($caller, @exports);
71 11         150 goto &Sub::Inject::sub_inject;
72             }
73             }
74              
75 0     0 0 0 sub role_provider {'Jojo::Role'}
76              
77             sub with_roles {
78 6     6 1 9101 Carp::croak 'Jojo::Role 0.4.0+ is required for roles' unless ROLES;
79 6         20 my ($self, @roles) = @_;
80              
81 6 100       41 return Jojo::Role->create_class_with_roles($self, @roles)
82             unless my $class = Scalar::Util::blessed $self;
83              
84 1         9 return Jojo::Role->apply_roles_to_object($self, @roles);
85             }
86              
87             BEGIN {
88 5     5   40 %EXPORT_TAGS = (-base => [qw(has with)], -role => [qw(has)], -strict => [],);
89              
90             %EXPORT_GEN = (
91             has => sub {
92 11         21 my (undef, $target) = @_;
93 10     10   789 return sub { Mojo::Base::attr($target, @_) }
94 11         60 },
95             with => sub { # dummy
96 0         0 return sub { Carp::croak 'Jojo::Role 0.4.0+ is required for roles' }
97 0         0 },
98 5         34 );
99              
100 5         11 return unless ROLES;
101              
102 5         13 push @{$EXPORT_TAGS{-role}}, @{$Jojo::Role::EXPORT_TAGS{-role}};
  5         11  
  5         23  
103              
104             $EXPORT_GEN{$_} = $Jojo::Role::EXPORT_GEN{$_}
105 5         10 for @{$Jojo::Role::EXPORT_TAGS{-role}};
  5         585  
106             }
107              
108             sub _generate_subs {
109 11     11   26 my ($class, $target) = (shift, shift);
110 11         25 return map { my $cb = $EXPORT_GEN{$_}; $_ => $class->$cb($target) } @_;
  30         92  
  30         83  
111             }
112              
113             1;
114              
115             #pod =encoding utf8
116             #pod
117             #pod =head1 SYNOPSIS
118             #pod
119             #pod package Cat {
120             #pod use Jojo::Base -base; # requires perl 5.18+
121             #pod
122             #pod has name => 'Nyan';
123             #pod has ['age', 'weight'] => 4;
124             #pod }
125             #pod
126             #pod package Tiger {
127             #pod use Jojo::Base 'Cat';
128             #pod
129             #pod has friend => sub { Cat->new };
130             #pod has stripes => 42;
131             #pod }
132             #pod
133             #pod package main;
134             #pod use Jojo::Base -strict;
135             #pod
136             #pod my $mew = Cat->new(name => 'Longcat');
137             #pod say $mew->age;
138             #pod say $mew->age(3)->weight(5)->age;
139             #pod
140             #pod my $rawr = Tiger->new(stripes => 38, weight => 250);
141             #pod say $rawr->tap(sub { $_->friend->name('Tacgnol') })->weight;
142             #pod
143             #pod =head1 DESCRIPTION
144             #pod
145             #pod L works kind of like L but C is imported
146             #pod as lexical subroutine.
147             #pod
148             #pod L, like L, is a simple base class designed
149             #pod to be effortless and powerful.
150             #pod
151             #pod # Enables "strict", "warnings", "utf8" and Perl 5.18 and "lexical_subs" features
152             #pod use Jojo::Base -strict;
153             #pod use Jojo::Base -base;
154             #pod use Jojo::Base 'SomeBaseClass';
155             #pod use Jojo::Base -role;
156             #pod
157             #pod All four forms save a lot of typing. Note that role support depends on
158             #pod L (0.4.0+).
159             #pod
160             #pod # use Jojo::Base -strict;
161             #pod use strict;
162             #pod use warnings;
163             #pod use utf8;
164             #pod use feature ':5.18';
165             #pod use experimental 'lexical_subs';
166             #pod use IO::Handle ();
167             #pod
168             #pod # use Jojo::Base -base;
169             #pod use strict;
170             #pod use warnings;
171             #pod use utf8;
172             #pod use feature ':5.18';
173             #pod use experimental 'lexical_subs';
174             #pod use IO::Handle ();
175             #pod push @ISA, 'Jojo::Base';
176             #pod state sub has { ... } # attributes
177             #pod state sub with { ... } # role composition
178             #pod
179             #pod # use Jojo::Base 'SomeBaseClass';
180             #pod use strict;
181             #pod use warnings;
182             #pod use utf8;
183             #pod use feature ':5.18';
184             #pod use experimental 'lexical_subs';
185             #pod use IO::Handle ();
186             #pod require SomeBaseClass;
187             #pod push @ISA, 'SomeBaseClass';
188             #pod state sub has { ... } # attributes
189             #pod state sub with { ... } # role composition
190             #pod
191             #pod # use Jojo::Base -role;
192             #pod use strict;
193             #pod use warnings;
194             #pod use utf8;
195             #pod use feature ':5.18';
196             #pod use experimental 'lexical_subs';
197             #pod use IO::Handle ();
198             #pod use Jojo::Role;
199             #pod state sub has { ... } # attributes
200             #pod
201             #pod On Perl 5.20+ you can also append a C<-signatures> flag to all four forms and
202             #pod enable support for L.
203             #pod
204             #pod # Also enable signatures
205             #pod use Jojo::Base -strict, -signatures;
206             #pod use Jojo::Base -base, -signatures;
207             #pod use Jojo::Base 'SomeBaseClass', -signatures;
208             #pod use Jojo::Base -role, -signatures;
209             #pod
210             #pod This will also disable experimental warnings on versions of Perl where this
211             #pod feature was still experimental.
212             #pod
213             #pod =head2 DIFFERENCES FROM C
214             #pod
215             #pod =over 4
216             #pod
217             #pod =item *
218             #pod
219             #pod All functions are exported as lexical subs
220             #pod
221             #pod =item *
222             #pod
223             #pod Role support depends on L instead of L
224             #pod
225             #pod =item *
226             #pod
227             #pod C is exported alongside C (when L is available)
228             #pod
229             #pod =item *
230             #pod
231             #pod Feature bundle for Perl 5.18 is enabled by default, instead of 5.10
232             #pod
233             #pod =item *
234             #pod
235             #pod Support for L is enabled
236             #pod by default
237             #pod
238             #pod =back
239             #pod
240             #pod =head1 CAVEATS
241             #pod
242             #pod =over 4
243             #pod
244             #pod =item *
245             #pod
246             #pod L requires perl 5.18 or newer
247             #pod
248             #pod =item *
249             #pod
250             #pod Because a lexical sub does not behave like a package import,
251             #pod some code may need to be enclosed in blocks to avoid warnings like
252             #pod
253             #pod "state" subroutine &has masks earlier declaration in same scope at...
254             #pod
255             #pod =back
256             #pod
257             #pod =head1 SEE ALSO
258             #pod
259             #pod L, L.
260             #pod
261             #pod =cut
262              
263             __END__