File Coverage

blib/lib/Mojo/Base.pm
Criterion Covered Total %
statement 121 126 96.0
branch 73 78 93.5
condition 12 18 66.6
subroutine 104 104 100.0
pod 4 4 100.0
total 314 330 95.1


line stmt bran cond sub pod time code
1             package Mojo::Base;
2              
3 101     24066   41111 use strict;
  101         785  
  101         2961  
4 101     4953   492 use warnings;
  101         192  
  101         2435  
5 101     4756   59326 use utf8;
  101         1483  
  101         512  
6 101     4020   4391 use feature ':5.16';
  101         245  
  101         48454  
7 101     3823   52858 use mro;
  101         81392  
  101         1939  
8              
9             # No imports because we get subclassed, a lot!
10 101     3823   5216 use Carp ();
  101         2043  
  101         3940  
11 101     339   538 use Scalar::Util ();
  101         184  
  101         14705  
12              
13             # Defer to runtime so Mojo::Util can use "-strict"
14             require Mojo::Util;
15              
16             # Role support requires Role::Tiny 2.000001+
17 101     339   652 use constant ROLES => !!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 });
  101         183  
  101         166  
  101         57105  
  101         482151  
  101         30779  
18              
19             # async/await support requires Future::AsyncAwait 0.52+
20             use constant ASYNC => $ENV{MOJO_NO_ASYNC}
21             ? 0
22 101 50   339   775 : !!(eval { require Future::AsyncAwait; Future::AsyncAwait->VERSION('0.52'); 1 });
  101         3662  
  101         425  
  101         49070  
  0         0  
  0         0  
23              
24             # Protect subclasses using AUTOLOAD
25       238     sub DESTROY { }
26              
27             sub attr {
28 10710     10948 1 29168 my ($self, $attrs, $value, %kv) = @_;
29 10710 50 66     54686 return unless (my $class = ref $self || $self) && $attrs;
      33        
30              
31 10710 100 100     31231 Carp::croak 'Default has to be a code reference or constant value' if ref $value && ref $value ne 'CODE';
32 10709 100       24597 Carp::croak 'Unsupported attribute option' if grep { $_ ne 'weak' } keys %kv;
  536         2405  
33              
34             # Weaken
35 10708 100       22999 if ($kv{weak}) {
36 535         814 state %weak_names;
37 535 100       1669 unless ($weak_names{$class}) {
38 533         1597 my $names = $weak_names{$class} = [];
39             my $sub = sub {
40 3966     4204   112092 my $self = shift->next::method(@_);
        1821321      
        1594676      
        1579192      
        10222      
        10222      
        10222      
        10222      
        10222      
        10222      
41 3966   66     23630 ref $self->{$_} and Scalar::Util::weaken $self->{$_} for @$names;
42 3966         11617 return $self;
43 533         2299 };
44 533         2609 Mojo::Util::monkey_patch(my $base = $class . '::_Base', 'new', $sub);
45 101     339   647 no strict 'refs';
  101         3653  
  101         128538  
46 533         1255 unshift @{"${class}::ISA"}, $base;
  533         8302  
47             }
48 535 100       1591 push @{$weak_names{$class}}, ref $attrs eq 'ARRAY' ? @$attrs : $attrs;
  535         2397  
49             }
50              
51 10708 100       13995 for my $attr (@{ref $attrs eq 'ARRAY' ? $attrs : [$attrs]}) {
  10708         32798  
52 13989 100       55323 Carp::croak qq{Attribute "$attr" invalid} unless $attr =~ /^[a-zA-Z_]\w*$/;
53              
54             # Very performance-sensitive code with lots of micro-optimizations
55 13988         18764 my $sub;
56 13988 100       33322 if ($kv{weak}) {
    100          
    100          
57 586 100       1422 if (ref $value) {
58             $sub = sub {
59             return
60             exists $_[0]{$attr}
61             ? $_[0]{$attr}
62 28843 100 66 29081   135918 : (ref($_[0]{$attr} = $value->($_[0])) && Scalar::Util::weaken($_[0]{$attr}), $_[0]{$attr})
    100   1562482      
        1551250      
        1561952      
        10222      
        10222      
        10155      
        10155      
        10155      
        10155      
        10155      
63             if @_ == 1;
64 1161 100       5397 ref($_[0]{$attr} = $_[1]) and Scalar::Util::weaken($_[0]{$attr});
65 1161         3761 $_[0];
66 431         2053 };
67             }
68             else {
69             $sub = sub {
70 31001 100   49921   119397 return $_[0]{$attr} if @_ == 1;
        1321001      
        1210919      
        10155      
        10050      
        9841      
71 1015 100       5059 ref($_[0]{$attr} = $_[1]) and Scalar::Util::weaken($_[0]{$attr});
72 1015         2250 $_[0];
73 155         768 };
74             }
75             }
76             elsif (ref $value) {
77             $sub = sub {
78 401813 100   461895   1572352 return exists $_[0]{$attr} ? $_[0]{$attr} : ($_[0]{$attr} = $value->($_[0])) if @_ == 1;
    100   788427      
        1143601      
        1502485      
        1902798      
        1340711      
        1207319      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9841      
        9647      
        9647      
        9647      
        9647      
        9647      
        9647      
        6931      
        3779      
        2401      
79 29913         54759 $_[0]{$attr} = $_[1];
80 29913         69947 $_[0];
81 6301         21247 };
82             }
83             elsif (defined $value) {
84             $sub = sub {
85 68589 100   1895024   336104 return exists $_[0]{$attr} ? $_[0]{$attr} : ($_[0]{$attr} = $value) if @_ == 1;
    100   1955475      
        808639      
        793573      
        10222      
86 7217         18412 $_[0]{$attr} = $_[1];
87 7217         17137 $_[0];
88 3081         10402 };
89             }
90             else {
91 4020 100   2128134   14923 $sub = sub { return $_[0]{$attr} if @_ == 1; $_[0]{$attr} = $_[1]; $_[0] };
  186661     2247425   2788597  
  37077     2398049   103780  
  37077     2024956   114316  
        2429316      
        647977      
        304410      
        138709      
        74707      
        13057      
92             }
93 13988         35565 Mojo::Util::monkey_patch($class, $attr, $sub);
94             }
95             }
96              
97             sub import {
98 10414     2234787   253091 my ($class, $caller) = (shift, caller);
99 10414 100       813996 return unless my @flags = @_;
100              
101             # Mojo modules are strict!
102 5338         92683 $_->import for qw(strict warnings utf8);
103 5338         318922 feature->import(':5.16');
104              
105 5338         20142 while (my $flag = shift @flags) {
106              
107             # Base
108 7334 100       246100 if ($flag eq '-base') { push @flags, $class }
  1989 100       6423  
    50          
    100          
    100          
    100          
109              
110             # Role
111             elsif ($flag eq '-role') {
112 2         3 Carp::croak 'Role::Tiny 2.000001+ is required for roles' unless ROLES;
113 2     2176528   19 Mojo::Util::monkey_patch($caller, 'has', sub { attr($caller, @_) });
  1         108  
114 2 50       198 eval "package $caller; use Role::Tiny; 1" or die $@;
  1         12  
  1         3  
  1         6  
  1         9  
  1         2  
  1         13  
115             }
116              
117             # async/await
118             elsif ($flag eq '-async_await') {
119 0         0 Carp::croak 'Future::AsyncAwait 0.52+ is required for async/await' unless ASYNC;
120 0         0 require Mojo::Promise;
121 0         0 Future::AsyncAwait->import_into($caller, future_class => 'Mojo::Promise');
122             }
123              
124             # Signatures (Perl 5.20+)
125             elsif ($flag eq '-signatures') {
126 6 50       28 Carp::croak 'Subroutine signatures require Perl 5.20+' if $] < 5.020;
127 6         2769 require experimental;
128 6         17638 experimental->import($_) for qw(signatures postderef);
129             }
130              
131             # Module
132             elsif ($flag !~ /^-/) {
133 101     339   2471 no strict 'refs';
  101         1833  
  101         65577  
134 4105 100       33736 require(Mojo::Util::class_to_path($flag)) unless $flag->can('new');
135 4105         8564 push @{"${caller}::ISA"}, $flag;
  4105         47353  
136 4105     2058127   24324 Mojo::Util::monkey_patch($caller, 'has', sub { attr($caller, @_) });
  10702     2517831   27534  
        1810301      
        2034929      
        1916520      
        1541147      
        10271      
        10222      
        10222      
        10222      
137             }
138              
139 1         200 elsif ($flag ne '-strict') { Carp::croak "Unsupported flag: $flag" }
140             }
141             }
142              
143             sub new {
144 53519     1542555 1 819300 my $class = shift;
145 53519 100 66     321485 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  383 100       4578  
146             }
147              
148             sub tap {
149 22     1482454 1 74 my ($self, $cb) = (shift, shift);
150 22         96 $_->$cb(@_) for $self;
151 22         159 return $self;
152             }
153              
154             sub with_roles {
155 13     1507288 1 20590 Carp::croak 'Role::Tiny 2.000001+ is required for roles' unless ROLES;
156 13         32 my ($self, @roles) = @_;
157 13 100       38 return $self unless @roles;
158              
159 11 100       46 return Role::Tiny->create_class_with_roles($self, map { /^\+(.+)$/ ? "${self}::Role::$1" : $_ } @roles)
  14 100       90  
160             unless my $class = Scalar::Util::blessed $self;
161              
162 1 100       5 return Role::Tiny->apply_roles_to_object($self, map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @roles);
  2         19  
163             }
164              
165             1;
166              
167             =encoding utf8
168              
169             =head1 NAME
170              
171             Mojo::Base - Minimal base class for Mojo projects
172              
173             =head1 SYNOPSIS
174              
175             package Cat;
176             use Mojo::Base -base;
177              
178             has name => 'Nyan';
179             has ['age', 'weight'] => 4;
180              
181             package Tiger;
182             use Mojo::Base 'Cat';
183              
184             has friend => sub { Cat->new };
185             has stripes => 42;
186              
187             package main;
188             use Mojo::Base -strict;
189              
190             my $mew = Cat->new(name => 'Longcat');
191             say $mew->age;
192             say $mew->age(3)->weight(5)->age;
193              
194             my $rawr = Tiger->new(stripes => 38, weight => 250);
195             say $rawr->tap(sub { $_->friend->name('Tacgnol') })->weight;
196              
197             =head1 DESCRIPTION
198              
199             L is a simple base class for L projects with fluent interfaces.
200              
201             # Automatically enables "strict", "warnings", "utf8" and Perl 5.16 features
202             use Mojo::Base -strict;
203             use Mojo::Base -base;
204             use Mojo::Base 'SomeBaseClass';
205             use Mojo::Base -role;
206              
207             All four forms save a lot of typing. Note that role support depends on L (2.000001+).
208              
209             # use Mojo::Base -strict;
210             use strict;
211             use warnings;
212             use utf8;
213             use feature ':5.16';
214             use mro;
215              
216             # use Mojo::Base -base;
217             use strict;
218             use warnings;
219             use utf8;
220             use feature ':5.16';
221             use mro;
222             push @ISA, 'Mojo::Base';
223             sub has { Mojo::Base::attr(__PACKAGE__, @_) }
224              
225             # use Mojo::Base 'SomeBaseClass';
226             use strict;
227             use warnings;
228             use utf8;
229             use feature ':5.16';
230             use mro;
231             require SomeBaseClass;
232             push @ISA, 'SomeBaseClass';
233             sub has { Mojo::Base::attr(__PACKAGE__, @_) }
234              
235             # use Mojo::Base -role;
236             use strict;
237             use warnings;
238             use utf8;
239             use feature ':5.16';
240             use mro;
241             use Role::Tiny;
242             sub has { Mojo::Base::attr(__PACKAGE__, @_) }
243              
244             On Perl 5.20+ you can also use the C<-signatures> flag with all four forms and enable support for L
245             signatures|perlsub/"Signatures">.
246              
247             # Also enable signatures
248             use Mojo::Base -strict, -signatures;
249             use Mojo::Base -base, -signatures;
250             use Mojo::Base 'SomeBaseClass', -signatures;
251             use Mojo::Base -role, -signatures;
252              
253             If you have L 0.52+ installed you can also use the C<-async_await> flag to activate the C
254             and C keywords to deal much more efficiently with promises. Note that this feature is B and might
255             change without warning!
256              
257             # Also enable async/await
258             use Mojo::Base -strict, -async_await;
259             use Mojo::Base -base, -signatures, -async_await;
260              
261             This will also disable experimental warnings on versions of Perl where this feature was still experimental.
262              
263             =head1 FLUENT INTERFACES
264              
265             Fluent interfaces are a way to design object-oriented APIs around method chaining to create domain-specific languages,
266             with the goal of making the readability of the source code close to written prose.
267              
268             package Duck;
269             use Mojo::Base -base, -signatures;
270              
271             has 'name';
272              
273             sub quack ($self) {
274             my $name = $self->name;
275             say "$name: Quack!"
276             }
277              
278             L will help you with this by having all attribute accessors created with L (or L) return
279             their invocant (C<$self>) whenever they are used to assign a new attribute value.
280              
281             Duck->new->name('Donald')->quack;
282              
283             In this case the C attribute accessor is called on the object created by Cnew>. It assigns a new
284             attribute value and then returns the C object, so the C method can be called on it afterwards. These
285             method chains can continue until one of the methods called does not return the C object.
286              
287             =head1 FUNCTIONS
288              
289             L implements the following functions, which can be imported with the C<-base> flag or by setting a base
290             class.
291              
292             =head2 has
293              
294             has 'name';
295             has ['name1', 'name2', 'name3'];
296             has name => 'foo';
297             has name => sub {...};
298             has ['name1', 'name2', 'name3'] => 'foo';
299             has ['name1', 'name2', 'name3'] => sub {...};
300             has name => sub {...}, weak => 1;
301             has name => undef, weak => 1;
302             has ['name1', 'name2', 'name3'] => sub {...}, weak => 1;
303              
304             Create attributes for hash-based objects, just like the L method.
305              
306             =head1 METHODS
307              
308             L implements the following methods.
309              
310             =head2 attr
311              
312             $object->attr('name');
313             SubClass->attr('name');
314             SubClass->attr(['name1', 'name2', 'name3']);
315             SubClass->attr(name => 'foo');
316             SubClass->attr(name => sub {...});
317             SubClass->attr(['name1', 'name2', 'name3'] => 'foo');
318             SubClass->attr(['name1', 'name2', 'name3'] => sub {...});
319             SubClass->attr(name => sub {...}, weak => 1);
320             SubClass->attr(name => undef, weak => 1);
321             SubClass->attr(['name1', 'name2', 'name3'] => sub {...}, weak => 1);
322              
323             Create attribute accessors for hash-based objects, an array reference can be used to create more than one at a time.
324             Pass an optional second argument to set a default value, it should be a constant or a callback. The callback will be
325             executed at accessor read time if there's no set value, and gets passed the current instance of the object as first
326             argument. Accessors can be chained, that means they return their invocant when they are called with an argument.
327              
328             These options are currently available:
329              
330             =over 2
331              
332             =item weak
333              
334             weak => $bool
335              
336             Weaken attribute reference to avoid L and memory leaks.
337              
338             =back
339              
340             =head2 new
341              
342             my $object = SubClass->new;
343             my $object = SubClass->new(name => 'value');
344             my $object = SubClass->new({name => 'value'});
345              
346             This base class provides a basic constructor for hash-based objects. You can pass it either a hash or a hash reference
347             with attribute values.
348              
349             =head2 tap
350              
351             $object = $object->tap(sub {...});
352             $object = $object->tap('some_method');
353             $object = $object->tap('some_method', @args);
354              
355             Tap into a method chain to perform operations on an object within the chain (also known as a K combinator or Kestrel).
356             The object will be the first argument passed to the callback, and is also available as C<$_>. The callback's return
357             value will be ignored; instead, the object (the callback's first argument) will be the return value. In this way,
358             arbitrary code can be used within (i.e., spliced or tapped into) a chained set of object method calls.
359              
360             # Longer version
361             $object = $object->tap(sub { $_->some_method(@args) });
362              
363             # Inject side effects into a method chain
364             $object->foo('A')->tap(sub { say $_->foo })->foo('B');
365              
366             =head2 with_roles
367              
368             my $new_class = SubClass->with_roles('SubClass::Role::One');
369             my $new_class = SubClass->with_roles('+One', '+Two');
370             $object = $object->with_roles('+One', '+Two');
371              
372             Create a new class with one or more L roles. If called on a class returns the new class, or if called on an
373             object reblesses the object into the new class. For roles following the naming scheme C you
374             can use the shorthand C<+RoleName>. Note that role support depends on L (2.000001+).
375              
376             # Create a new class with the role "SubClass::Role::Foo" and instantiate it
377             my $new_class = SubClass->with_roles('+Foo');
378             my $object = $new_class->new;
379              
380             =head1 SEE ALSO
381              
382             L, L, L.
383              
384             =cut