File Coverage

blib/lib/Deeme/Obj.pm
Criterion Covered Total %
statement 93 98 94.9
branch 52 58 89.6
condition 9 15 60.0
subroutine 20 21 95.2
pod 3 9 33.3
total 177 201 88.0


line stmt bran cond sub pod time code
1             package Deeme::Obj;
2             ### This is Mojo::Base
3 5     5   1368 use strict;
  5         8  
  5         140  
4 5     5   21 use warnings;
  5         8  
  5         121  
5 5     5   4640 use utf8;
  5         42  
  5         24  
6              
7             #use feature ();
8              
9             our $feature = eval {
10             require feature;
11             feature->import();
12             1;
13             };
14              
15             # No imports because we get subclassed, a lot!
16 5     5   312 use Carp ();
  5         9  
  5         77  
17              
18             # Only Perl 5.14+ requires it on demand
19 5     5   4615 use IO::Handle ();
  5         39544  
  5         811  
20              
21             # Protect subclasses using AUTOLOAD
22 0     0   0 sub DESTROY { }
23              
24             sub import {
25 34     34   36981 my $class = shift;
26 34 100       4921 return unless my $flag = shift;
27              
28             # Base
29 21 100 66     173 if ( $flag eq '-base' ) { $flag = $class }
  10 100       18  
    100          
30              
31             # Strict
32 4         8 elsif ( $flag eq '-strict' ) { $flag = undef }
33              
34             # Module
35             elsif ( ( my $file = $flag ) && !$flag->can('new') ) {
36 5         41 $file =~ s!::|'!/!g;
37 5         2160 require "$file.pm";
38             }
39              
40             # ISA
41 21 100       64 if ($flag) {
42 17         38 my $caller = caller;
43 5     5   40 no strict 'refs';
  5         7  
  5         3060  
44 17         19 push @{"${caller}::ISA"}, $flag;
  17         226  
45 17     12   69 *{"${caller}::has"} = sub { attr( $caller, @_ ) };
  17         108  
  12         53  
46             }
47              
48             # Mojo modules are strict!
49 21         554 $_->import for qw(strict warnings utf8);
50 21 50       734 if ($feature) {
51 0         0 feature->import(':5.10');
52             }
53             }
54              
55             sub attr {
56 17     17 1 713 my ( $self, $attrs, $default ) = @_;
57 17 50 66     141 return unless ( my $class = ref $self || $self ) && $attrs;
      33        
58              
59 17 100 100     260 Carp::croak 'Default has to be a code reference or constant value'
60             if ref $default && ref $default ne 'CODE';
61              
62 16 100       23 for my $attr ( @{ ref $attrs eq 'ARRAY' ? $attrs : [$attrs] } ) {
  16         70  
63 18 100       218 Carp::croak qq{Attribute "$attr" invalid}
64             unless $attr =~ /^[a-zA-Z_]\w*$/;
65              
66             # Header (check arguments)
67 17         50 my $code = "package $class;\nsub $attr {\n if (\@_ == 1) {\n";
68              
69             # No default value (return value)
70 17 100       41 unless ( defined $default ) { $code .= " return \$_[0]{'$attr'};" }
  13         31  
71              
72             # Default value
73             else {
74              
75             # Return value
76 4         9 $code
77             .= " return \$_[0]{'$attr'} if exists \$_[0]{'$attr'};\n";
78              
79             # Return default value
80 4         6 $code .= " return \$_[0]{'$attr'} = ";
81 4 100       10 $code .=
82             ref $default eq 'CODE'
83             ? '$default->($_[0]);'
84             : '$default;';
85             }
86              
87             # Store value
88 17         33 $code .= "\n }\n \$_[0]{'$attr'} = \$_[1];\n";
89              
90             # Footer (return invocant)
91 17         25 $code .= " \$_[0];\n}";
92              
93 17 50       55 warn "-- Attribute $attr in $class\n$code\n\n"
94             if $ENV{DEEME_OBJ_DEBUG};
95 17 50   199 0 1917 Carp::croak "Deeme::Obj error: $@" unless eval "$code;1";
  199 100   17 0 473  
  197 100   18 0 885  
  2 100   3 0 7  
  2 100   1 0 5  
  17 100   3 0 81  
  11 100   3   45  
  6 50   2   35  
  6 100   6   17  
  18 100       48  
  17 100       59  
  1 100       6  
  1 100       4  
  1 100       5  
  3         548  
  2         9  
  1         6  
  1         4  
  1         4  
  1         38  
  1         13  
  0         0  
  0         0  
  3         559  
  2         9  
  1         5  
  1         4  
  1         4  
  3         40  
  2         11  
  1         6  
  1         3  
  1         3  
  2         31  
  1         6  
  1         14  
  1         4  
  6         119  
  3         17  
  3         8  
  3         17  
96             }
97             }
98              
99             sub new {
100 23     23 1 53 my $class = shift;
101 23 50 33     313 bless @_ ? @_ > 1 ? {@_} : { %{ $_[0] } } : {}, ref $class || $class;
  0 100       0  
102             }
103              
104             sub tap {
105 2     2 1 20 my ( $self, $cb ) = @_;
106 2         10 $_->$cb for $self;
107 2         55 return $self;
108             }
109              
110             1;
111              
112             =encoding utf8
113              
114             =head1 NAME
115              
116             Deeme::Obj - Minimal base class for Deeme
117              
118             =head1 SYNOPSIS
119              
120             package Cat;
121             use Deeme::Obj -base;
122              
123             has name => 'Nyan';
124             has [qw(birds mice)] => 2;
125              
126             package Tiger;
127             use Deeme::Obj 'Cat';
128              
129             has friend => sub { Cat->new };
130             has stripes => 42;
131              
132             package main;
133             use Deeme::Obj -strict;
134              
135             my $mew = Cat->new(name => 'Longcat');
136             say $mew->mice;
137             say $mew->mice(3)->birds(4)->mice;
138              
139             my $rawr = Tiger->new(stripes => 23, mice => 0);
140             say $rawr->tap(sub { $_->friend->name('Tacgnol') })->mice;
141              
142             =head1 DESCRIPTION
143              
144             L is a simple base class for L, a fork of L.
145              
146             # Automatically enables "strict", "warnings", "utf8" and Perl 5.10 features
147             use Deeme::Obj -strict;
148             use Deeme::Obj -base;
149             use Deeme::Obj 'SomeBaseClass';
150              
151             All three forms save a lot of typing.
152              
153             # use Deeme::Obj -strict;
154             use strict;
155             use warnings;
156             use utf8;
157             use feature ':5.10';
158             use IO::Handle ();
159              
160             # use Deeme::Obj -base;
161             use strict;
162             use warnings;
163             use utf8;
164             use feature ':5.10';
165             use IO::Handle ();
166             use Deeme::Obj;
167             push @ISA, 'Deeme::Obj';
168             sub has { Deeme::Obj::attr(__PACKAGE__, @_) }
169              
170             # use Deeme::Obj 'SomeBaseClass';
171             use strict;
172             use warnings;
173             use utf8;
174             use feature ':5.10';
175             use IO::Handle ();
176             require SomeBaseClass;
177             push @ISA, 'SomeBaseClass';
178             use Deeme::Obj;
179             sub has { Deeme::Obj::attr(__PACKAGE__, @_) }
180              
181             =head1 FUNCTIONS
182              
183             L implements the following functions like L, which can be imported with
184             the C<-base> flag or by setting a base class.
185              
186             =head2 has
187              
188             has 'name';
189             has [qw(name1 name2 name3)];
190             has name => 'foo';
191             has name => sub {...};
192             has [qw(name1 name2 name3)] => 'foo';
193             has [qw(name1 name2 name3)] => sub {...};
194              
195             Create attributes for hash-based objects, just like the L method.
196              
197             =head1 METHODS
198              
199             L implements the following methods.
200              
201             =head2 attr
202              
203             $object->attr('name');
204             BaseSubClass->attr('name');
205             BaseSubClass->attr([qw(name1 name2 name3)]);
206             BaseSubClass->attr(name => 'foo');
207             BaseSubClass->attr(name => sub {...});
208             BaseSubClass->attr([qw(name1 name2 name3)] => 'foo');
209             BaseSubClass->attr([qw(name1 name2 name3)] => sub {...});
210              
211             Create attribute accessor for hash-based objects, an array reference can be
212             used to create more than one at a time. Pass an optional second argument to
213             set a default value, it should be a constant or a callback. The callback will
214             be executed at accessor read time if there's no set value. Accessors can be
215             chained, that means they return their invocant when they are called with an
216             argument.
217              
218             =head2 new
219              
220             my $object = BaseSubClass->new;
221             my $object = BaseSubClass->new(name => 'value');
222             my $object = BaseSubClass->new({name => 'value'});
223              
224             This base class provides a basic constructor for hash-based objects. You can
225             pass it either a hash or a hash reference with attribute values.
226              
227             =head2 tap
228              
229             $object = $object->tap(sub {...});
230              
231             K combinator, tap into a method chain to perform operations on an object
232             within the chain. The object will be the first argument passed to the callback
233             and is also available as C<$_>.
234              
235             =head1 DEBUGGING
236              
237             You can set the C environment variable to get some advanced
238             diagnostics information printed to C.
239              
240             DEEME_OBJ_DEBUG=1
241              
242             =head1 SEE ALSO
243              
244             L, L.
245              
246             =cut