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