File Coverage

blib/lib/JIP/Object.pm
Criterion Covered Total %
statement 125 125 100.0
branch 50 50 100.0
condition 8 8 100.0
subroutine 30 31 96.7
pod 6 10 60.0
total 219 224 97.7


line stmt bran cond sub pod time code
1             package JIP::Object;
2              
3 1     1   2625 use 5.006;
  1         3  
4 1     1   4 use strict;
  1         1  
  1         17  
5 1     1   3 use warnings;
  1         1  
  1         21  
6 1     1   4 use Carp qw(croak);
  1         1  
  1         48  
7 1     1   5 use Scalar::Util qw(blessed);
  1         1  
  1         43  
8 1     1   6 use English qw(-no_match_vars);
  1         1  
  1         6  
9              
10             our $VERSION = '0.031';
11             our $AUTOLOAD;
12              
13             my $maybe_set_subname = sub { $ARG[1]; };
14              
15             # Supported on Perl 5.22+
16             eval {
17             require Sub::Util;
18              
19             if (my $set_subname = Sub::Util->can('set_subname')) {
20             $maybe_set_subname = $set_subname;
21             }
22             };
23              
24             sub new {
25 12     12 1 8913 my ($class, %param) = @ARG;
26              
27 12 100       154 croak q{Class already blessed} if blessed $class;
28              
29 11         24 my $proto;
30 11 100       20 if (exists $param{'proto'}) {
31 2         4 $proto = $param{'proto'};
32              
33 2 100 100     93 croak q{Bad argument "proto"}
34             unless (blessed $proto || q{}) eq __PACKAGE__;
35             }
36              
37 10         23 return bless({}, $class)
38             ->_set_stash({})
39             ->_set_meta({})
40             ->set_proto($proto);
41             }
42              
43             sub has {
44 13     13 1 3618 my ($self, $attr, %param) = @ARG;
45              
46 13 100       104 croak q{Can't call "has" as a class method} unless blessed $self;
47              
48 12 100 100     143 croak q{Attribute not defined} unless defined $attr and length $attr;
49              
50 10         13 my @patches;
51              
52 10 100       11 for my $each_attr (@{ ref $attr eq 'ARRAY' ? $attr : [$attr] }) {
  10         28  
53 11 100       98 croak sprintf(q{Attribute "%s" invalid}, $each_attr)
54             unless $each_attr =~ m{^[a-zA-Z_]\w*$}x;
55              
56 10         11 my %patch;
57              
58             # getter
59             $patch{_define_name_of_getter($each_attr, \%param)} = sub {
60 15     15   18 my $self = shift;
61 15         20 return $self->_stash->{$attr};
62 10         41 };
63              
64             # setter
65             {
66 10         27 my $method_name = _define_name_of_setter($each_attr, \%param);
  10         16  
67              
68 10 100       19 if (exists $param{'default'}) {
69 2         3 my $default_value = $param{'default'};
70              
71             $patch{$method_name} = sub {
72 6     6   7 my $self = shift;
73              
74 6 100       14 if (@ARG == 1) {
    100          
75 4         6 $self->_stash->{$attr} = shift;
76             }
77             elsif (ref $default_value eq 'CODE') {
78 1         7 $self->_stash->{$attr} = $maybe_set_subname->(
79             'default_value',
80             $default_value,
81             )->($self);
82             }
83             else {
84 1         2 $self->_stash->{$attr} = $default_value;
85             }
86              
87 6         20 return $self;
88 2         15 };
89             }
90             else {
91             $patch{$method_name} = sub {
92 8     8   13 my ($self, $value) = @ARG;
93 8         12 $self->_stash->{$attr} = $value;
94 8         33 return $self;
95 8         22 };
96             }
97             }
98              
99 10         21 push @patches, \%patch;
100             }
101              
102 9         16 for my $each_patch (@patches) {
103 10         11 while (my ($method_name, $code) = each %{ $each_patch }) {
  30         65  
104 20         74 $self->_meta->{$method_name} = $maybe_set_subname->($method_name, $code);
105             }
106             }
107              
108 9         54 return $self;
109             }
110              
111             sub method {
112 10     10 1 4909 my ($self, $method_name, $code) = @ARG;
113              
114 10 100       101 croak q{Can't call "method" as a class method}
115             unless blessed $self;
116              
117 9 100 100     131 croak q{First argument must be a non empty string}
118             unless defined $method_name and length $method_name;
119              
120 7 100       81 croak sprintf(q{First argument "%s" invalid}, $method_name)
121             unless $method_name =~ m{^[a-zA-Z_]\w*$}x;
122              
123 6 100       61 croak q{Second argument must be a code ref}
124             unless ref($code) eq 'CODE';
125              
126 5         22 $self->_meta->{$method_name} = $maybe_set_subname->($method_name, $code);
127              
128 5         20 return $self;
129             }
130              
131             sub own_method {
132 45     45 1 71 my ($self, $method_name) = @ARG;
133              
134 45 100       71 return unless exists $self->_meta->{$method_name};
135              
136 39         49 return $self->_meta->{$method_name};
137             }
138              
139             # http://perldoc.perl.org/perlobj.html#Default-UNIVERSAL-methods
140             sub isa {
141 1     1   956 no warnings 'misc';
  1         2  
  1         81  
142 10     10 0 114 goto &UNIVERSAL::isa;
143             }
144              
145             sub DOES {
146             # DOES is equivalent to isa by default
147 3     3 0 9 goto &isa;
148             }
149              
150             sub VERSION {
151 1     1   4 no warnings 'misc';
  1         2  
  1         61  
152 3     3 0 2336 goto &UNIVERSAL::VERSION;
153             }
154              
155             sub can {
156 17     17 0 1677 my ($self, $method_name) = @ARG;
157              
158 17 100       41 if (blessed $self) {
159 1     1   5 no warnings 'misc';
  1         1  
  1         42  
160 10         121 goto &UNIVERSAL::can;
161             }
162             else {
163 7         8 my $code;
164 1     1   5 no warnings 'misc';
  1         1  
  1         593  
165 7         17 $code = UNIVERSAL::can($self, $method_name);
166              
167 7         16 return $code;
168             }
169             }
170              
171       0     sub DESTROY {}
172              
173             sub AUTOLOAD {
174 38     38   4218 my ($self) = @ARG;
175              
176 38 100       165 croak q{Can't call "AUTOLOAD" as a class method} unless blessed $self;
177              
178 37         178 my ($package, $method_name) = ($AUTOLOAD =~ m{^(.+)::([^:]+)$}x);
179 37         47 undef $AUTOLOAD;
180              
181 37 100       51 if (defined(my $code = $self->own_method($method_name))) {
    100          
182 35         81 goto &$code;
183             }
184             elsif (defined(my $proto = $self->proto)) {
185 1         2 shift @ARG;
186 1         6 $proto->$method_name(@ARG);
187             }
188             else {
189 1         55 croak(sprintf q{Can't locate object method "%s" in this instance}, $method_name);
190             }
191             }
192              
193             sub proto {
194 4     4 1 289 return $ARG[0]->{'proto'};
195             }
196              
197             sub set_proto {
198 11     11 1 14 $ARG[0]->{'proto'} = $ARG[1];
199 11         24 return $ARG[0];
200             }
201              
202             # private methods
203             sub _define_name_of_getter {
204 14     14   2255 my ($attr, $param) = @ARG;
205              
206 14         19 my $method_name;
207              
208 14 100       24 if (exists $param->{'get'}) {
209 13         14 my $getter = $param->{'get'};
210              
211 13 100       24 if ($getter eq q{+}) {
    100          
212 8         10 $method_name = $attr;
213             }
214             elsif ($getter eq q{-}) {
215 3         6 $method_name = q{_}. $attr;
216             }
217             else {
218 2         4 $method_name = $getter;
219             }
220             }
221             else {
222 1         2 $method_name = $attr;
223             }
224              
225 14         34 return $method_name;
226             }
227              
228             sub _define_name_of_setter {
229 14     14   2269 my ($attr, $param) = @ARG;
230              
231 14         15 my $method_name;
232              
233 14 100       22 if (exists $param->{'set'}) {
234 13         17 my $setter = $param->{'set'};
235              
236 13 100       21 if ($setter eq q{+}) {
    100          
237 8         12 $method_name = q{set_}. $attr;
238             }
239             elsif ($setter eq q{-}) {
240 3         6 $method_name = q{_set_}. $attr;
241             }
242             else {
243 2         3 $method_name = $setter;
244             }
245             }
246             else {
247 1         3 $method_name = q{set_}. $attr;
248             }
249              
250 14         26 return $method_name;
251             }
252              
253             sub _meta {
254 109     109   227 return $ARG[0]->{'meta'};
255             }
256             sub _set_meta {
257 10     10   12 $ARG[0]->{'meta'} = $ARG[1];
258 10         17 return $ARG[0];
259             }
260              
261             sub _stash {
262 29     29   79 return $ARG[0]->{'stash'};
263             }
264             sub _set_stash {
265 10     10   17 $ARG[0]->{'stash'} = $ARG[1];
266 10         19 return $ARG[0];
267             }
268              
269             1;
270              
271             __END__