File Coverage

blib/lib/HO/accessor.pm
Criterion Covered Total %
statement 130 136 95.5
branch 56 64 87.5
condition 11 17 64.7
subroutine 19 21 90.4
pod 1 1 100.0
total 217 239 90.7


line stmt bran cond sub pod time code
1             package HO::accessor;
2             # +++++++++++++++++++
3 13     13   133047 use strict; use warnings;
  13     13   39  
  13         331  
  13         60  
  13         21  
  13         470  
4             our $VERSION='0.053';
5             # +++++++++++++++++++
6              
7             ; use Class::ISA ()
8 13     13   5501 ; use Package::Subroutine ()
  13         24624  
  13         264  
9 13     13   5171 ; use Carp ()
  13         9535  
  13         251  
10              
11 13     13   77 ; our %classes
  13         23  
  13         17947  
12             ; my %accessors
13             ; my %methods
14              
15             ; our %type = ('@'=>sub () {[]}, '%'=>sub () {{}}, '$'=>sub () {undef})
16              
17             ; our %init =
18             ( 'hash' => sub
19             { my ($self,%args) = @_
20             ; while(my ($method,$value)=each(%args))
21             { my $access = "_$method"
22             ; $self->[$self->$access] = $value
23             }
24             ; return $self
25             },
26             'hashref' => sub
27             { my ($self,$args) = @_
28             ; while(my ($method,$value)=each(%$args))
29             { my $access = "_$method"
30             ; $self->[$self->$access] = $value
31             }
32             ; return $self
33             }
34             )
35              
36             ; our %ro_accessor =
37             ( '$' => sub { my ($n,$class) = @_
38             ; my $idx = HO::accessor::_value_of($class, "_$n")
39             ; return sub ()
40 14 100   14   2297 { Carp::confess("Not a class method '$n'.")
41             unless ref($_[0])
42 13         52 ; $_[0]->[$idx]
43             }
44             }
45             , '@' => sub { my ($n,$class) = @_
46             ; my $ai = HO::accessor::_value_of($class, "_$n")
47             ; return sub
48 7     5   34 { my ($obj,$idx) = @_
49 7 100       24 ; if(@_==1)
50 7         22 { return @{$obj->[$ai]}
  4         31  
51             }
52             else
53 1         4 { return $obj->[$ai]->[$idx]
54             }
55             }}
56             , '%' => sub { my ($n,$class) = @_
57             ; my $idx = HO::accessor::_value_of($class, "_$n")
58             ; return sub
59 4     4   14 { my ($obj,$key) = @_
60 4         28 ; (@_==1) ? {%{$obj->[$idx]}}
61 4 50       18 : $obj->[$idx]->{$key}
62             }
63             }
64             )
65              
66             ; our %rw_accessor =
67             ( '$' => sub { my ($n,$class) = @_
68             ; my $idx = HO::accessor::_value_of($class, "_$n")
69             ; return sub
70 10     10   1253 { my ($obj,$val) = @_
71 10 100       39 ; Carp::confess("Not a class method '$n'.")
72             unless ref($obj)
73 9 100       43 ; return $obj->[$idx] if @_==1
74 2         6 ; $obj->[$idx] = $val
75 2         5 ; return $obj
76             }
77             }
78             , '@' => sub { my ($n,$class) = @_
79             ; my $ai = HO::accessor::_value_of($class, "_$n")
80             ; return sub
81 22     22   62 { my ($obj,$idx,$val) = @_
82 22 50       51 ; Carp::confess("Not a class method '$n'.")
83             unless ref $obj
84 22 100       66 ; if(@_==1) # get values
    100          
    50          
85             { # etwas mehr Zugriffsschutz da keine Ref
86             # einfache Anwendung in bool Kontext
87 10         13 ; return @{$obj->[$ai]}
  10         57  
88             }
89             elsif(@_ == 2)
90 4 100       10 { unless(ref $idx eq 'ARRAY')
91 1         5 { return $obj->[$ai]->[$idx] # get one index
92             }
93             else
94 3         5 { $obj->[$ai] = $idx # set complete array
95 3         8 ; return $obj
96             }
97             }
98             elsif(@_==3)
99 8 100       24 { if(ref($idx))
    100          
    100          
100 5 100       18 { if($val eq '<')
    100          
101 1         2 { $$idx = shift @{$obj->[$ai]}
  1         3  
102             }
103             elsif($val eq '>')
104 1         2 { $$idx = pop @{$obj->[$ai]}
  1         3  
105             }
106             else
107 3 100       13 { if(@$val == 0)
    100          
    50          
108 1         2 { @$idx = splice(@{$obj->[$ai]})
  1         5  
109             }
110             elsif(@$val == 1)
111 1         2 { @$idx = splice(@{$obj->[$ai]},$val->[0]);
  1         4  
112             }
113             elsif(@$val == 2)
114 1         3 { @$idx = splice(@{$obj->[$ai]},$val->[0],$val->[1]);
  1         4  
115             }
116             }
117             }
118             elsif($idx eq '<')
119 1         2 { push @{$obj->[$ai]}, $val
  1         3  
120             }
121             elsif($idx eq '>')
122 1         3 { unshift @{$obj->[$ai]}, $val
  1         3  
123             }
124             else
125 1         3 { $obj->[$ai]->[$idx] = $val # set one index
126             }
127 8         21 ; return $obj
128             }
129             }
130             }
131             , '%' => sub { my ($n,$class) = @_
132             ; my $idx = HO::accessor::_value_of($class, "_$n")
133 11     11   26 ; return sub { my ($obj,$key) = @_
134 11 100       33 ; if(@_==1)
    100          
135 5         21 { return $obj->[$idx] # for a hash an reference is easier to handle
136             }
137             elsif(@_==2)
138 5 100       14 { if(ref($key) eq 'HASH')
139 1         2 { $obj->[$idx] = $key
140 1         5 ; return $obj
141             }
142             else
143 4         17 { return $obj->[$idx]->{$key}
144             }
145             }
146             else
147 1         2 { shift(@_)
148 1         3 ; my @kv = @_
149 1         4 ; while(@kv)
150 1         4 { my ($k,$v) = splice(@kv,0,2)
151 1         3 ; $obj->[$idx]->{$k} = $v
152             }
153 1         5 ; return $obj
154             }
155             }}
156             )
157              
158             ; our $class
159              
160             ; my $object_builder = sub
161             { my ($obj,$constructor,$args) = @_
162             ; foreach my $typedefault (@$constructor)
163             { push @{$obj}, ref($typedefault) ? $typedefault->($obj,$args)
164             : $typedefault
165             }
166             }
167              
168             ; sub import
169 31     31   90 { my ($package,$ac,$methods,$init,$new) = @_
170 31         40 ; our %classes
171 31   50     79 ; $ac ||= []
172              
173 31   66     73 ; my $caller = $HO::accessor::class || CORE::caller
174              
175 31 100 66     113 ; Carp::croak "HO::accessor::import already called for class $caller."
176             if Package::Subroutine->isdefined($caller,'new') && $new
177              
178 30 100       453 ; $classes{$caller} = [] unless defined $classes{$caller}
179 30         45 ; push @{$classes{$caller}}, @$ac
  30         121  
180              
181 30         95 ; my @build = reverse Class::ISA::self_and_super_path($caller)
182             ; my @constructor
183 30         730 ; my @class_accessors
184              
185 30         52 ; my $count=0
186 30         59 ; foreach my $class (@build)
187 34 50       98 { $classes{$class} or next
188 34 100       51 ; my @acc=@{$classes{$class}} or next
  34         116  
189 22         63 ; while (@acc)
190 51         104 { my ($accessor,$type)=splice(@acc,0,2)
191 51 100       137 ; my $proto = ref($type) eq 'CODE' ? $type : $type{$type}
192 51 50       126 ; unless(ref $proto eq 'CODE')
193 0         0 { Carp::carp("Unknown property type '$type', in setup for class $caller.")
194 0     0   0 ; $proto=sub{undef}
195 0         0 }
196 51         67 ; my $val=$count
197 100     100   2561 ; my $acc=sub {$val}
198 51         128 ; push @class_accessors, $accessor
  51         82  
199 51         125 ; $accessors{$caller}{$accessor}=$acc
200 51         77 ; $constructor[$acc->()] = $proto
201 51         135 ; $count++
202             }
203             }
204             # FIXME: Die init Methode sollte Zugriff auf $self haben können.
205 13         3600 ; { no strict 'refs'
  30         54  
206 13 100   13   102 ; if($new)
  13         50  
  30         62  
207 28         106 { *{"${caller}::new"}=
208             ($init || $caller->can('init')) ?
209             sub
210 10     10   2136 { my ($self,@args)=@_
211 10   33     61 ; my $obj = bless [], ref $self || $self
212 10         34 ; $object_builder->($obj,\@constructor,\@args)
213 10         30 ; return $obj->init(@args)
214             }
215             : sub
216 20     20   9232 { my ($self,@args)=@_
217 20   66     118 ; my $obj = bless [], ref $self || $self
218 20         83 ; $object_builder->($obj,\@constructor,\@args)
219 20         63 ; return $obj
220             }
221 28 100 100     433 }
222              
223 30         59 ; foreach my $acc (@class_accessors)
224 51         99 { *{"${caller}::${acc}"} = $accessors{$caller}{$acc}
  51         194  
225             }
226              
227 30         64 ; my %class_methods = @$methods
228 30         71 ; $methods{$caller} = \%class_methods
229             }
230              
231             # setup init method
232 30 100       261 ; if($init)
233 2 50       6 { unless(ref($init) eq 'CODE' )
234 2         5 { $init = $init{$init}
235 2 50       5 ; unless(defined $init)
236 0         0 { Carp::croak("There is no init defined for init argument $init.")
237             }
238             }
239 13         2560 ; no strict 'refs'
240 13     13   88 ; *{"${caller}::init"}= $init
  13         26  
  2         3  
  2         10  
241             }
242             }
243              
244             # Package Method
245             ; sub accessors_for_class
246 0     0 1 0 { my ($self,$class)=@_
247 0         0 ; return $classes{$class}
248             }
249              
250             # Package Function
251             ; sub _value_of
252 32     32   60 { my ($class,$accessorname) = @_
253 32         73 ; return $accessors{$class}{$accessorname}->()
254             }
255              
256             ; sub _methods_code
257 1     1   2 { my ($class,$methodname) = @_
258 1         3 ; return $methods{$class}{$methodname}
259             }
260              
261             ; 1
262              
263             __END__