File Coverage

blib/lib/Rope/Object.pm
Criterion Covered Total %
statement 119 132 90.1
branch 69 86 80.2
condition 33 43 76.7
subroutine 16 21 76.1
pod 0 5 0.0
total 237 287 82.5


line stmt bran cond sub pod time code
1             package Rope::Object;
2              
3 47     47   321 use strict;
  47         97  
  47         1698  
4 47     47   237 use warnings;
  47         94  
  47         2641  
5              
6 47     47   23506 use Const::XS qw/make_readonly/;
  47         31715  
  47         114298  
7              
8             sub TIEHASH {
9 118     118   363 my ($class, $obj) = @_;
10 118   50     692 my $self = bless $obj || {}, $class;
11             $self->{properties}->{ROPE_init} = {
12 118     118   418 value => sub { $self->init }
13 118         1048 };
14 118         492 return $self;
15             }
16              
17             sub init {
18 118     118 0 291 my ($self) = @_;
19             $self->set_value(
20             $_,
21             $self->{properties}->{$_}->{value},
22             $self->{properties}->{$_}
23 118         228 ) for keys %{$self->{properties}};
  118         981  
24 117         585 $self->compile();
25 117         380 delete $self->{properties}->{ROPE_init};
26 117         347 $self;
27             }
28              
29             sub compile {
30 117     117 0 295 my ($self) = @_;
31 117         245 $self->{keys} = scalar keys %{$self->{properties}};
  117         346  
32             $self->{sort_keys} = [sort {
33             $self->{properties}->{$a}->{index} <=> $self->{properties}->{$b}->{index}
34 117         264 } grep { $self->{properties}->{$_}->{enumerable} } keys %{$self->{properties}}];
  391         1091  
  587         1539  
  117         461  
35 117 50       468 if ($self->{requires}) {
36 117         233 for (keys %{$self->{requires}}) {
  117         410  
37             die sprintf "Failed to instantiate %s object requires property %s", $self->{name}, $_
38             unless $self->{properties}->{$_}
39 6 50 33     34 && defined $self->{properties}->{$_}->{value};
40             }
41             }
42 117         226 return $self;
43             }
44              
45             sub set_value {
46 641     641 0 5373 my ($self, $key, $value, $spec) = @_;
47 641 100       1405 if ($spec->{trigger}) {
48 13         39 $value = $spec->{trigger}->($value);
49             }
50 641 100 100     2067 if (ref($value || "") ne 'CODE') {
51 422         799 for (qw/before around after/) {
52 1266 100       3419 if ($spec->{$_}) {
53 9         25 my $val = $spec->{$_}->($value);
54 9 50       57 if (defined $val) {
55 9         20 $value = $val;
56             }
57             }
58             }
59             }
60 641 100 66     1961 if ($spec->{type} && defined $value) {
61 18         182 $value = eval {
62             $spec->{coerce_type}
63             ? $spec->{type}->coerce($value)
64 18 50       110 : $spec->{type}->($value);
65             };
66 18 100       694 if ($@) {
67 1         13 my @caller = caller(1);
68 1 50       5 if ($caller[0] eq 'Rope::Object') {
69 0         0 die sprintf("Failed to instantiate object (%s) property (%s) failed type validation. %s", $self->{name}, $key, $@);
70             }
71 1         9 die sprintf("Cannot set property (%s) in object (%s) failed type validation on line %s file %s: %s", $key, $self->{name}, $caller[2], $caller[1], $@);
72             }
73             }
74              
75 640 100       1502 if ($spec->{readonly}) {
76 6         21 make_readonly($value);
77             }
78              
79 640 100       1280 if ($spec->{handles_via}) {
80 4         265 eval "require $spec->{handles_via}";
81 4         17 my $href = ref $value;
82 4 50       22 $spec->{value} = $spec->{handles_via}->new($href eq 'ARRAY' ? @{$value} : $href eq 'HASH' ? %{$value} : $value);
  2 100       15  
  2         13  
83             } else {
84 636         1260 $spec->{value} = $value;
85             }
86              
87 640 100 100     1517 if ($spec->{required} && ! defined $spec->{value}) {
88 1         18 die sprintf "Required property (%s) in object (%s) not set", $key, $self->{name};
89             }
90 639         1775 return $spec->{value};
91             }
92            
93             sub STORE {
94 79     79   226 my ($self, $key, $value) = @_;
95              
96 79 100       249 if ($key eq 'locked') {
97 4         8 $self->{locked} = $value;
98 4         13 return;
99             }
100 75         212 my $k = $self->{properties}->{$key};
101              
102 75 100       225 if ($k) {
    100          
103 64 100       185 if ($k->{private}) {
104 1         4 my $priv = $self->private_names;
105 1 50       4 if ( $self->current_caller !~ m/^($priv)$/) {
106 1         14 die "Cannot access Object ($self->{name}) property ($key) as it is private";
107             }
108             }
109              
110 63 100       1259 if ($k->{writeable}) {
    100          
111 36         107 $self->set_value($key, $value, $k);
112             } elsif ($k->{configurable}) {
113 16 50 50     130 if ((ref($value) || "") eq (ref($k->{value}) || "")) {
      50        
114 16         97 $self->set_value($key, $value, $k);
115             } else {
116 0         0 die "Cannot change Object ($self->{name}) property ($key) type";
117             }
118             } else {
119 11         127 die "Cannot set Object ($self->{name}) property ($key) it is only readable";
120             }
121             } elsif (! $self->{locked}) {
122             $self->{properties}->{$key} = {
123             ((ref $value || "") eq 'HASH' && grep { defined $value->{$_} } qw/initable writeable configurable enumerable/) ? (
124             index => ++$self->{keys},
125 1         5 %{$value}
126             ) : (
127             value => $value,
128             initable => 1,
129             writeable => 1,
130             configurable => 1,
131             enumerable => 1,
132             index => ++$self->{keys}
133             )
134 7 100 100     86 };
135 7         14 push @{$self->{sort_keys}}, $key;
  7         23  
136             } else {
137 4         66 die "Object ($self->{name}) is locked you cannot extend with new properties";
138             }
139 58         192 return $self;
140             }
141            
142             sub FETCH {
143 926     926   6462 my ($self, $key) = @_;
144 926   100     4371 my $k = $self->{properties}->{$key} || $self->{handles}->{$key} && $self->{properties}->{$self->{handles}->{$key}};
145 926 100       3782 return undef unless defined $k->{value};
146 792 100       1859 if ($k->{private}) {
147 7         24 my $priv = $self->private_names;
148 7 100       19 if ( $self->current_caller !~ m/^($priv)$/) {
149 2         28 die "Cannot access Object ($self->{name}) property ($key) as it is private";
150             }
151             }
152            
153 790 100 100     4327 if (!$k->{writeable} && !$k->{configurable} && (ref($k->{value}) || '') eq 'CODE') {
      100        
      100        
154 226 100 100     1544 if ($k->{before} || $k->{after} || $k->{around}) {
      100        
155             return sub {
156 14     14   40 my (@params) = @_;
157 14         55 my @new_params;
158 14 100       50 @new_params = $k->{before}->(@params) if $k->{before};
159 14 100       43 @params = @new_params if scalar @new_params;
160 14 100       50 if ($k->{around}) {
161 1         4 @params = $k->{around}->($k->{value}, @params);
162             } else {
163 13         44 @params = $k->{value}->(@params);
164             }
165 14 100       1779 if ($k->{after}) {
166 12         53 @new_params = ($k->{after}->(@params));
167 10 50       115 @params = @new_params if scalar @new_params;
168             }
169 12 100       159 return wantarray ? @params : $params[0];
170 28         295 };
171             }
172             }
173            
174 762 100 66     2217 if ($self->{handles}->{$key} && !$self->{properties}->{ROPE_init}) {
175 20         32 my $meth = $k->{handles}->{$key};
176 20 50       38 if ($k->{value}) {
177 20     13   118 return sub { $k->{value}->$meth(@_) };
  13         59  
178             }
179             }
180              
181 742         3647 return $k->{value};
182             }
183            
184             sub FIRSTKEY {
185 11     11   59 goto &NEXTKEY;
186             }
187            
188             sub NEXTKEY {
189 32     32   55 return (each @{$_[0]->{sort_keys}})[1];
  32         168  
190             }
191            
192             sub EXISTS {
193 0     0   0 exists $_[0]->{properties}->{$_[1]};
194             }
195            
196             sub DELETE {
197 0     0   0 my $k = $_[0]->{properties}->{$_[1]};
198 0 0       0 if ($k->{delete_trigger}) {
199 0         0 $k->{delete_trigger}->($k->{value});
200             }
201 0 0 0     0 my $del = !$_[0]->{locked} && $k->{writeable} ? delete $_[0]->{properties}->{$_[1]} : undef;
202 0 0       0 $_[0]->compile() if $del;
203 0         0 return $del;
204             }
205            
206             sub CLEAR {
207 0     0   0 return;
208             #%{$_[0]->{properties}} = ()
209             }
210            
211             sub SCALAR {
212 0     0   0 scalar keys %{$_[0]->{properties}}
  0         0  
213             }
214              
215       0     sub DESTROY { }
216              
217             sub private_names {
218 8     8 0 14 my $self = shift;
219 8 50       69 return $self->{name} . ($self->{with} ? ('|' . join('|', @{$self->{with}})) : '') . ($self->{extends} ? ('|' . join('|', @{$self->{extends}})) : '');
  0 100       0  
  3         15  
220             }
221              
222             sub current_caller {
223 8     8 0 17 my ($n, $caller) = (0, '');
224 8         40 while (my $call = scalar caller($n)) {
225 22 100       123 if ($call !~ m/Rope(::(Object|Autoload|Monkey))?/) {
226 8         199 return $call;
227             }
228 14         47 $n++;
229             }
230             }
231              
232             1;
233              
234             __END__