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