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     23895   42311 use strict;
  101         758  
  101         3018  
4 101     4953   524 use warnings;
  101         193  
  101         2529  
5 101     4756   61052 use utf8;
  101         1473  
  101         552  
6 101     4020   4565 use feature ':5.16';
  101         203  
  101         54423  
7 101     3823   55139 use mro;
  101         83378  
  101         2319  
8              
9             # No imports because we get subclassed, a lot!
10 101     3823   3236 use Carp ();
  101         2086  
  101         4093  
11 101     339   2330 use Scalar::Util ();
  101         188  
  101         16496  
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   630 use constant ROLES => !!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 });
  101         193  
  101         171  
  101         59969  
  101         503302  
  101         31200  
18              
19             # async/await support requires Future::AsyncAwait 0.52+
20             use constant ASYNC => $ENV{MOJO_NO_ASYNC}
21             ? 0
22 101 50   339   2712 : !!(eval { require Future::AsyncAwait; Future::AsyncAwait->VERSION('0.52'); 1 });
  101         1808  
  101         545  
  101         51666  
  0         0  
  0         0  
23              
24             # Protect subclasses using AUTOLOAD
25       238     sub DESTROY { }
26              
27             sub attr {
28 10710     10948 1 29433 my ($self, $attrs, $value, %kv) = @_;
29 10710 50 66     55822 return unless (my $class = ref $self || $self) && $attrs;
      33        
30              
31 10710 100 100     31873 Carp::croak 'Default has to be a code reference or constant value' if ref $value && ref $value ne 'CODE';
32 10709 100       24920 Carp::croak 'Unsupported attribute option' if grep { $_ ne 'weak' } keys %kv;
  536         2508  
33              
34             # Weaken
35 10708 100       23113 if ($kv{weak}) {
36 535         898 state %weak_names;
37 535 100       1750 unless ($weak_names{$class}) {
38 533         1742 my $names = $weak_names{$class} = [];
39             my $sub = sub {
40 3966     4204   113422 my $self = shift->next::method(@_);
        1818372      
        1592254      
        1576770      
        10222      
        10222      
        10222      
        10222      
        10222      
        10222      
41 3966   66     23196 ref $self->{$_} and Scalar::Util::weaken $self->{$_} for @$names;
42 3966         12342 return $self;
43 533         2379 };
44 533         2721 Mojo::Util::monkey_patch(my $base = $class . '::_Base', 'new', $sub);
45 101     339   631 no strict 'refs';
  101         3480  
  101         130985  
46 533         1235 unshift @{"${class}::ISA"}, $base;
  533         8687  
47             }
48 535 100       1526 push @{$weak_names{$class}}, ref $attrs eq 'ARRAY' ? @$attrs : $attrs;
  535         2496  
49             }
50              
51 10708 100       15773 for my $attr (@{ref $attrs eq 'ARRAY' ? $attrs : [$attrs]}) {
  10708         33504  
52 13989 100       55167 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         18839 my $sub;
56 13988 100       34406 if ($kv{weak}) {
    100          
    100          
57 586 100       1603 if (ref $value) {
58             $sub = sub {
59             return
60             exists $_[0]{$attr}
61             ? $_[0]{$attr}
62 28568 100 66 28806   142016 : (ref($_[0]{$attr} = $value->($_[0])) && Scalar::Util::weaken($_[0]{$attr}), $_[0]{$attr})
    100   1560060      
        1548828      
        1559530      
        10222      
        10222      
        10155      
        10155      
        10155      
        10155      
        10155      
63             if @_ == 1;
64 1161 100       5825 ref($_[0]{$attr} = $_[1]) and Scalar::Util::weaken($_[0]{$attr});
65 1161         3976 $_[0];
66 431         2233 };
67             }
68             else {
69             $sub = sub {
70 31001 100   49887   124057 return $_[0]{$attr} if @_ == 1;
        1319128      
        1209132      
        10155      
        10050      
        9841      
71 1015 100       5207 ref($_[0]{$attr} = $_[1]) and Scalar::Util::weaken($_[0]{$attr});
72 1015         2184 $_[0];
73 155         748 };
74             }
75             }
76             elsif (ref $value) {
77             $sub = sub {
78 401178 100   460985   1624508 return exists $_[0]{$attr} ? $_[0]{$attr} : ($_[0]{$attr} = $value->($_[0])) if @_ == 1;
    100   786882      
        1141421      
        1499670      
        1899348      
        1339009      
        1205703      
        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 29875         53808 $_[0]{$attr} = $_[1];
80 29875         71191 $_[0];
81 6301         22302 };
82             }
83             elsif (defined $value) {
84             $sub = sub {
85 68561 100   1891546   341171 return exists $_[0]{$attr} ? $_[0]{$attr} : ($_[0]{$attr} = $value) if @_ == 1;
    100   1952238      
        807658      
        792654      
        10222      
86 7217         19506 $_[0]{$attr} = $_[1];
87 7217         18373 $_[0];
88 3081         10786 };
89             }
90             else {
91 4020 100   2124590   13858 $sub = sub { return $_[0]{$attr} if @_ == 1; $_[0]{$attr} = $_[1]; $_[0] };
  186519     2243910   2823550  
  37073     2394392   109266  
  37073     2021934   125817  
        2426010      
        647138      
        304206      
        138647      
        74673      
        13051      
92             }
93 13988         34864 Mojo::Util::monkey_patch($class, $attr, $sub);
94             }
95             }
96              
97             sub import {
98 10414     2231623   251929 my ($class, $caller) = (shift, caller);
99 10414 100       847435 return unless my @flags = @_;
100              
101             # Mojo modules are strict!
102 5338         94875 $_->import for qw(strict warnings utf8);
103 5338         329911 feature->import(':5.16');
104              
105 5338         20511 while (my $flag = shift @flags) {
106              
107             # Base
108 7334 100       261534 if ($flag eq '-base') { push @flags, $class }
  1989 100       6540  
    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     2173358   14 Mojo::Util::monkey_patch($caller, 'has', sub { attr($caller, @_) });
  1         88  
114 2 50       160 eval "package $caller; use Role::Tiny; 1" or die $@;
  1         10  
  1         2  
  1         4  
  1         18  
  1         1  
  1         11  
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       37 Carp::croak 'Subroutine signatures require Perl 5.20+' if $] < 5.020;
127 6         2730 require experimental;
128 6         18710 experimental->import($_) for qw(signatures postderef);
129             }
130              
131             # Module
132             elsif ($flag !~ /^-/) {
133 101     339   2606 no strict 'refs';
  101         2129  
  101         69471  
134 4105 100       35820 require(Mojo::Util::class_to_path($flag)) unless $flag->can('new');
135 4105         8824 push @{"${caller}::ISA"}, $flag;
  4105         48558  
136 4105     2055064   25649 Mojo::Util::monkey_patch($caller, 'has', sub { attr($caller, @_) });
  10702     2514277   28444  
        1807629      
        2031764      
        1913497      
        1538759      
        10271      
        10222      
        10222      
        10222      
137             }
138              
139 1         250 elsif ($flag ne '-strict') { Carp::croak "Unsupported flag: $flag" }
140             }
141             }
142              
143             sub new {
144 53519     1540161 1 806663 my $class = shift;
145 53519 100 66     334665 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  383 100       5203  
146             }
147              
148             sub tap {
149 22     1480060 1 85 my ($self, $cb) = (shift, shift);
150 22         99 $_->$cb(@_) for $self;
151 22         160 return $self;
152             }
153              
154             sub with_roles {
155 13     1504894 1 14888 Carp::croak 'Role::Tiny 2.000001+ is required for roles' unless ROLES;
156 13         27 my ($self, @roles) = @_;
157 13 100       45 return $self unless @roles;
158              
159 11 100       33 return Role::Tiny->create_class_with_roles($self, map { /^\+(.+)$/ ? "${self}::Role::$1" : $_ } @roles)
  14 100       91  
160             unless my $class = Scalar::Util::blessed $self;
161              
162 1 100       3 return Role::Tiny->apply_roles_to_object($self, map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @roles);
  2         17  
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