File Coverage

blib/lib/Rope.pm
Criterion Covered Total %
statement 389 431 90.2
branch 137 192 71.3
condition 46 77 59.7
subroutine 40 42 95.2
pod 9 11 81.8
total 621 753 82.4


line stmt bran cond sub pod time code
1             package Rope;
2              
3 53     53   5479679 use 5.006; use strict; use warnings;
  53     53   211  
  53     47   376  
  53         4192  
  53         1749  
  53         331  
  47         105  
  47         4299  
4             our $VERSION = '0.44';
5 47     47   24476 use Rope::Object;
  47         148  
  47         2025  
6 47     47   22294 use Rope::Pro;
  47         198  
  47         249084  
7             my (%META, %PRO);
8             our @ISA;
9              
10             BEGIN {
11             %PRO = Rope::Pro->new(
12             scope => sub {
13 118         723 my ($caller, $self, %props) = @_;
14 118         258 for my $prop (keys %{$props{properties}}) {
  118         599  
15 457 100 100     2323 if ($props{properties}{$prop}{value} && ref $props{properties}{$prop}{value} eq 'CODE') {
16 83         168 my $cb = $props{properties}{$prop}{value};
17 83         540 $props{properties}{$prop}{value} = sub { $cb->($META{initialised}{$caller}{${$self}->{identifier}}, @_) };
  65         220  
  65         329  
18             }
19              
20 457         905 for (qw/predicate clearer/) {
21 914 100       2258 if ($props{properties}->{$prop}->{$_}) {
22 16 100       33 my $prep = $_ eq 'predicate' ? 'has_' : 'clear_';
23 16         28 my $pred = $props{properties}->{$prop}->{$_};
24 16         23 my $ref = ref($pred);
25 16 100       57 if ( !$ref ) {
    100          
    50          
26             $props{properties}->{$pred =~ m/^\d+$/ ? "$prep$prop" : $pred} = {
27             value => $_ eq 'predicate'
28 4 50       13 ? sub { return defined $META{initialised}{$caller}{${$self}->{identifier}}->{$prop} ? 1 : '' }
  4         15  
29 4         7 : sub { $META{initialised}{$caller}{${$self}->{identifier}}->{$prop} = undef; 1; }
  4         9  
  4         8  
30 8 100       86 };
    100          
31             } elsif ($ref eq 'CODE') {
32             $props{properties}->{"$prep$prop"} = {
33 4         12 value => sub { $pred->($META{initialised}{$caller}{${$self}->{identifier}}, $prop) }
  4         21  
34 4         41 };
35             } elsif ($ref eq 'HASH') {
36 4         9 my $cb = $pred->{value};
37 4         20 $pred->{value} = sub { $cb->($META{initialised}{$caller}{${$self}->{identifier}}, $prop) };
  4         12  
  4         21  
38 4   33     31 $props{properties}->{$pred->{name} || "$prep$prop"} = $pred;
39             }
40             }
41             }
42 457         807 for (qw/trigger delete_trigger/) {
43 914         1480 my $trigger = $props{properties}{$prop}{$_};
44 914 100       1978 if (defined $trigger) {
45             $props{properties}{$prop}{$_} = sub {
46 13         31 $trigger->($META{initialised}{$caller}{${$self}->{identifier}}, @_)
  13         62  
47 5         50 };
48             }
49             }
50 457         765 for (qw/before after/) {
51 914         1473 my $mod = $props{properties}{$prop}{$_};
52 914 50 100     1892 if (defined $mod && scalar @{$mod}) {
  13         63  
53             my $cb = sub {
54 46         80 my $cb = shift;
55 46         89 $cb->($META{initialised}{$caller}{${$self}->{identifier}}, @_);
  46         216  
56 13         68 };
57             $props{properties}{$prop}{$_} = sub {
58 19         49 my (@params) = @_;
59 19         45 for (my $i = 0; $i < scalar @{$mod} - 1; $i++) {
  46         135  
60 29         122 my @new_params = ($cb->($mod->[$i], @params));
61 27 100       217 @params = @new_params if @new_params;
62             }
63 17         77 return $cb->($mod->[-1], @params);
64 13         146 };
65             }
66             }
67 457         794 my $mod = $props{properties}{$prop}{around};
68 457 50 100     1020 if (defined $mod && scalar @{$mod}) {
  2         7  
69             my $cb = sub {
70 5         10 my $cb = shift;
71 5         12 $cb->($META{initialised}{$caller}{${$self}->{identifier}}, @_);
  5         22  
72 2         12 };
73             $props{properties}{$prop}{around} = sub {
74 4         11 my ($orig, @params) = @_;
75 4   100     16 my $code = (ref($orig) || "") eq 'CODE';
76 4 100       14 @params = ($orig) if (!$code);
77 4         7 my @stack;
78 4         7 for (my $i = 0; $i < scalar @{$mod}; $i++) {
  9         24  
79 5         10 my $current = $mod->[$i];
80 5 100       43 my $next = $stack[-1] ? $stack[-1] : $code ? $orig : sub { $_[0] };
  3 100       40  
81             my $calling = sub {
82 5         26 return $cb->($current, $next, @_);
83 5         19 };
84 5         14 push @stack, $calling;
85             }
86 4         9 return $stack[-1]->(@params);
87 2         10 };
88             }
89 457         832 my $handles = $props{properties}{$prop}{handles};
90 457 100       1099 if ($handles) {
91 4 100       4 $props{handles} = {%{ $props{handles} || {} }, map { $_ => $prop } keys %{$handles}};
  4         43  
  16         31  
  4         7  
92             }
93             }
94 118         1261 return \%props;
95             },
96             clone => sub {
97 6139         8591 my $obj = shift;
98 6139         9249 my $ref = ref $obj;
99 6139 100       18391 return $obj if !$ref;
100 2004 100       3937 return [ map { $PRO{clone}->($_) } @{$obj} ] if $ref eq 'ARRAY';
  455         992  
  192         441  
101 1812 100       3652 return { map { $_ => $PRO{clone}->($obj->{$_}) } keys %{$obj} } if $ref eq 'HASH';
  5475         10372  
  1365         4156  
102 447         1417 return $obj;
103             },
104             set_prop => sub {
105 276         1144 my ($caller, $prop, %options) = @_;
106 276 100       855 if (exists $META{$caller}{properties}{$prop}) {
107 26   33     169 defined $options{$_} && do { $META{$caller}{properties}{$prop}{$_} = $options{$_} } for qw/builder trigger delete_trigger handles handles_via/;
  0         0  
108 26 50       65 if ($META{$caller}{properties}{$prop}{writeable}) {
    0          
109 26 50       80 $META{$caller}{properties}{$prop}{value} = $options{value} if exists $options{value};
110 26         112 $META{$caller}{properties}{$prop}{class} = $caller;
111             } elsif ($META{$caller}{properties}{$prop}{configurable}) {
112 0 0 0     0 if ((ref($META{$caller}{properties}{$prop}{value}) || "") eq (ref($options{value}) || "")) {
      0        
113 0 0       0 $META{$caller}{properties}{$prop}{value} = $options{value} if exists $options{value};
114 0         0 $META{$caller}{properties}{$prop}{class} = $caller;
115             } else {
116 0         0 die "Cannot inherit $META{$caller}{properties}{$prop}{class} and change property $prop type";
117             }
118             } else {
119 0         0 die "Cannot inherit $META{$caller}{properties}{$prop}{class} and change property $prop type";
120             }
121             } else {
122             $META{$caller}{properties}{$prop} = {
123             class => $options{class} || $caller,
124             index => ++$META{$caller}{keys},
125 250   66     3018 %options
126             };
127             }
128             },
129             requires => sub {
130 106         284 my ($caller) = shift;
131             return sub {
132 3 50 66 3   27 shift @_ if $_[0] && $_[0] eq $caller;
133 3         10 my (@requires) = @_;
134 3         25 $META{$caller}{requires}{$_}++ for (@requires);
135 106         626 };
136             },
137             readonly => sub {
138 101         261 my ($caller) = shift;
139             return sub {
140 3 50   3   223020 shift @_ if $_[0] eq $caller;
141 3         10 my ($prop, @options) = @_;
142 3 50       9 $prop = shift @options if ( @options > 1 );
143             $PRO{set_prop}(
144 3         12 $caller,
145             $prop,
146             value => $options[0],
147             enumerable => 0,
148             writeable => 0,
149             initable => 1,
150             readonly => 1,
151             );
152 101         483 };
153             },
154             private => sub {
155 101         209 my ($caller) = shift;
156             return sub {
157 1 50   1   267485 shift @_ if $_[0] eq $caller;
158 1         4 my ($prop, @options) = @_;
159 1 50       5 $prop = shift @options if ( @options > 1 );
160             $PRO{set_prop}(
161 1         8 $caller,
162             $prop,
163             value => $options[0],
164             enumerable => 0,
165             writeable => 0,
166             initable => 0,
167             private => 1,
168             );
169 101         491 };
170             },
171             function => sub {
172 104         281 my ($caller) = shift;
173             return sub {
174 20 100   20   203 shift @_ if $_[0] eq $caller;
175 20         66 my ($prop, @options) = @_;
176 20 50       71 $prop = shift @options if ( @options > 1 );
177             $PRO{set_prop}(
178 20         87 $caller,
179             $prop,
180             value => $options[0],
181             enumerable => 0,
182             writeable => 0,
183             initable => 0,
184             configurable => 0
185             );
186 104         726 };
187             },
188             properties => sub {
189 135         297 my ($caller) = shift;
190             return sub {
191 34 100   34   113 shift @_ if $_[0] eq $caller;
192 34         165 my (@properties) = @_;
193 34         96 while (@properties) {
194 131         289 my ($prop, $options) = (shift @properties, shift @properties);
195 131         347 my $ref = ref $options;
196 131 100 100     653 if (!$ref || $ref ne 'HASH' || ! grep { defined $options->{$_} }
  1476   66     2685  
197             qw/initable writeable builder enumerable configurable trigger clearer predicate delete_trigger value handles_via handles/
198             ) {
199 8         41 $options = {
200             initable => 1,
201             enumerable => 1,
202             writeable => 1,
203             configurable => 1,
204             value => $options
205             };
206             }
207              
208 131 100       386 for (ref $prop ? @{$prop} : ($prop)) {
  1         3  
209             $PRO{set_prop}(
210             $caller,
211             $_,
212 132         190 %{$options}
  132         421  
213             )
214             };
215             }
216 135         779 };
217             },
218             property => sub {
219 104         252 my ($caller) = shift;
220             return sub {
221 90 100   90   3496336 shift @_ if $_[0] eq $caller;
222 90         346 my ($prop, @options) = @_;
223 90 50       346 if (scalar @options % 2) {
224 0         0 $prop = shift @options;
225             }
226 90 100       314 for (ref $prop ? @{$prop} : ($prop)) {
  1         3  
227             $PRO{set_prop}(
228 91         345 $caller,
229             $_,
230             @options
231             );
232             }
233 104         587 };
234             },
235             prototyped => sub {
236 104         230 my ($caller) = shift;
237             return sub {
238 21 100   21   2610194 shift @_ if $_[0] eq $caller;
239 21         119 my (@proto) = @_;
240 21         94 while (@proto) {
241 27         84 my ($prop, $value) = (shift @proto, shift @proto);
242 27 100       140 for (ref $prop ? @{$prop} : ($prop)) {
  1         4  
243             $PRO{set_prop}(
244 29         153 $caller,
245             $_,
246             enumerable => 1,
247             writeable => 1,
248             configurable => 1,
249             initable => 1,
250             value => $value
251             );
252             }
253             }
254             }
255 104         491 },
256             with => sub {
257 102         207 my ($caller) = shift;
258             return sub {
259 3 50   3   16 shift @_ if $_[0] eq $caller;
260 3         9 my (@withs) = @_;
261 3         8 for my $with (@withs) {
262 3 100       15 if (!$META{$with}) {
263 1         4 (my $name = $with) =~ s!::!/!g;
264 1         3 $name .= ".pm";
265 1         514 CORE::require($name);
266             }
267 3         20 my $initial = $META{$caller};
268 3         11 my $merge = $PRO{clone}($META{$with});
269 3         8 push @{$merge->{with}}, $merge->{name};
  3         12  
270 3         9 $merge->{name} = $initial->{name};
271 3         7 $merge->{locked} = $initial->{locked};
272 3         7 for my $prop (keys %{$initial->{properties}}) {
  3         11  
273 0         0 $merge->{properties}->{$prop}->{index} = ++$merge->{keys};
274 0         0 my $modifier;
275 0         0 for (qw/before around after/) {
276 0 0       0 if ($initial->{properties}->{$prop}->{$_}) {
277 0         0 $modifier = 1;
278 0         0 unshift @{$merge->{properties}->{$prop}->{$_}}, @{$initial->{properties}->{$prop}->{$_}}
  0         0  
  0         0  
279             }
280             }
281 0 0       0 next if $modifier;
282 0 0       0 next if grep { $META{$with}->{properties}->{$prop}->{$_} } qw/before around after/;
  0         0  
283 0 0       0 if (scalar keys %{$merge->{properties}->{$prop}} > 1) {
  0         0  
284 0 0       0 if ($merge->{properties}->{writeable}) {
    0          
285 0         0 $merge->{properties}->{$prop} = $initial->{properties}->{$prop};
286             } elsif ($merge->{properties}->{configurable}) {
287 0 0 0     0 if ((ref($merge->{properties}->{$prop}->{value}) || "") eq (ref($initial->{properties}->{$prop}->{value} || ""))) {
      0        
288 0         0 $merge->{properties}->{$prop} = $initial->{properties}->{$prop};
289             } else {
290 0         0 die "Cannot include $with and change property $prop type";
291             }
292             } else {
293 0         0 die "Cannot include $with and override property $prop";
294             }
295             } else {
296 0         0 $merge->{properties}->{$prop} = $initial->{properties}->{$prop};
297             }
298             }
299 3         6 $merge->{requires} = {%{$merge->{requires}}, %{$initial->{requires}}};
  3         8  
  3         82  
300 3         24 $META{$caller} = $merge;
301             }
302             }
303 102         1071 },
304             extends => sub {
305 102         242 my ($caller) = shift;
306             return sub {
307 17 50   17   203 shift @_ if $_[0] eq $caller;
308 17         57 my (@extends) = @_;
309 17         49 for my $extend (@extends) {
310 17 100       112 if (!$META{$extend}) {
311 2         10 (my $name = $extend) =~ s!::!/!g;
312 2         8 $name .= ".pm";
313 2         975 CORE::require($name);
314             }
315 17         98 my $initial = $META{$caller};
316 17         79 my $merge = $PRO{clone}($META{$extend});
317 17         46 push @{$merge->{extends}}, $merge->{name};
  17         84  
318 17         66 $merge->{name} = $initial->{name};
319 17         49 $merge->{locked} = $initial->{locked};
320 17         36 for my $prop (keys %{$initial->{properties}}) {
  17         68  
321 0         0 $initial->{properties}->{$prop}->{index} = ++$merge->{keys};
322 0         0 for (qw/before around after/) {
323 0 0       0 unshift @{$merge->{properties}->{$prop}->{$_}}, @{$initial->{properties}->{$prop}->{$_}} if $initial->{properties}->{$prop}->{$_};
  0         0  
  0         0  
324             }
325 0 0       0 if ($merge->{properties}->{$prop}) {
326 0 0       0 if ($merge->{properties}->{writeable}) {
    0          
327 0         0 $merge->{properties}->{$prop} = $initial->{properties}->{$prop};
328             } elsif ($merge->{properties}->{configurable}) {
329 0 0 0     0 if ((ref($merge->{properties}->{$prop}->{value}) || "") eq (ref($initial->{properties}->{$prop}->{value} || ""))) {
      0        
330 0         0 $merge->{properties}->{$prop} = $initial->{properties}->{$prop};
331             } else {
332 0         0 die "Cannot inherit $extend and change property $prop type";
333             }
334             } else {
335 0         0 die "Cannot inherit $extend and override property $prop";
336             }
337             } else {
338 0         0 $merge->{properties}->{$prop} = $initial->{properties}->{$prop};
339             }
340             }
341 17         33 $merge->{requires} = {%{$merge->{requires}}, %{$initial->{requires}}};
  17         40  
  17         56  
342 17         60 my $isa = '@' . $caller . '::ISA';
343 17         1988 eval "push $isa, '$extend'";
344 17         176 $META{$caller} = $merge;
345             }
346             }
347 102         1091 },
348             before => sub {
349 104         218 my ($caller) = shift;
350             return sub {
351 3 50   3   33 shift @_ if $_[0] eq $caller;
352 3         5 my ($prop, $cb) = @_;
353 3         4 push @{$META{$caller}{properties}{$prop}{before}}, $cb;
  3         8  
354 104         745 };
355             },
356             around => sub {
357 104         260 my ($caller) = shift;
358             return sub {
359 3 50   3   40 shift @_ if $_[0] eq $caller;
360 3         8 my ($prop, $cb) = @_;
361 3         5 push @{$META{$caller}{properties}{$prop}{around}}, $cb;
  3         13  
362 104         695 };
363             },
364             after => sub {
365 104         209 my ($caller) = shift;
366             return sub {
367 14 100   14   76 shift @_ if $_[0] eq $caller;
368 14         48 my ($prop, $cb) = @_;
369 14         40 push @{$META{$caller}{properties}{$prop}{after}}, $cb;
  14         116  
370 104         768 };
371             },
372             locked => sub {
373 101         546 my ($caller) = shift;
374             return sub {
375 5     5   1721 my ($self, $locked) = @_;
376 5 100       16 if (ref $self) {
377 4         12 $self->{locked} = $locked;
378 4         12 return;
379             } else {
380 1         4 $META{$caller}{locked} = 1;
381             }
382 101         724 };
383             },
384             destroy => sub {
385 101         278 my ($caller) = shift;
386             return sub {
387 2     2   5 my ($self, $locked) = @_;
388 2 50       6 if (ref $self) {
389 2         6 return $self->DESTROY;
390             }
391 101         538 };
392             },
393             DESTROY => sub {
394 101         227 my ($caller) = shift;
395             return sub {
396 3     3   7 my ($self, $locked) = @_;
397 3 50       10 if (ref $self) {
398 3         6 delete $META{initialised}{$caller}{${$self}->{identifier}};
  3         13  
399 3         11 return;
400             }
401 101         534 };
402             },
403             new => sub {
404 133         266 my ($caller) = shift;
405             return sub {
406 119 50   119   642614 my ($class, %params) = (shift, scalar @_ == 1 ? %{$_[0]} : @_);
  0         0  
407             my $self = \{
408             prototype => {},
409 119         797 identifier => $META{initialised}{$caller}{identifier}++
410             };
411 119         343 $self = bless $self, $caller;
412 119         581 my $build = $PRO{clone}($META{$caller});
413 119         506 for (keys %params) {
414 24 50       94 next if $_ =~ m/^_/;
415 24 100       110 if ($build->{properties}->{$_}) {
416 22 100       76 if ($build->{properties}->{$_}->{initable}) {
417 21         102 $build->{properties}->{$_}->{value} = $params{$_};
418             } else {
419 1         17 die "Cannot initalise Object ($caller) property ($_) as initable is not set to true.";
420             }
421             } else {
422             $build->{properties}->{$_} = {
423             value => $params{$_},
424             initable => 1,
425             writeable => 1,
426             enumerable => 1,
427             configurable => 1,
428             index => ++$META{$caller}{keys}
429 2         13 };
430             }
431             }
432 118         253 for ( sort { $build->{properties}->{$a}->{index} <=> $build->{properties}->{$b}->{index} } keys %{ $build->{properties} } ) {
  551         1496  
  118         816  
433 448 100 100     1506 if ( !defined $build->{properties}->{$_}->{value} && defined $build->{properties}->{$_}->{builder}) {
434 19         74 my $builder = $build->{properties}->{$_}->{builder};
435 19 50       79 $build->{properties}->{$_}->{value} = ref $builder ? $builder->($build) : $caller->$builder($build);
436             }
437             }
438             exists $build->{properties}->{INITIALISE}
439 118 100 66     865 ? $build->{properties}->{INITIALISE}->{value}->($self, $build, \%params)
440             : $self->can('INITIALISE') && $self->INITIALISE($build, \%params);
441 118         280 tie %{${$self}->{prototype}}, 'Rope::Object', $PRO{scope}($caller, $self, %{$build});
  118         210  
  118         487  
  118         633  
442 118         440 $META{initialised}{$caller}->{${$self}->{identifier}} = $self;
  118         540  
443 118         395 $self->{ROPE_init}->();
444              
445             exists $build->{properties}->{INITIALISED}
446 117 50 66     965 ? $build->{properties}->{INITIALISED}->{value}->(\%params)
447             : $self->can('INITIALISED') && $self->INITIALISED(\%params);
448 117         1358 return $self;
449 133         1353 };
450             }
451 47     47   2816 );
452             }
453              
454             sub import {
455 108     108   17852 my ($pkg, $options, $caller) = (shift, {@_}, caller());
456 108 100       1030 return if $options->{no_import};
457 104 100       382 $caller = $options->{caller} if $options->{caller};
458 104 100       473 if (!$META{$caller}) {
459 80         561 $META{$caller} = {
460             name => $caller,
461             locked => 0,
462             properties => {},
463             requires => {},
464             keys => 0
465             };
466             }
467 104 100 33 246   873 $PRO{keyword}($caller, 'can', sub { ref $_[0] and ($_[0]->{$_[1]} || $META{$caller}->{properties}->{$_[1]}) || $_[0]->CORE::can($_[1]) });
  246   66     3817  
468 104     52   569 $PRO{keyword}($caller, '(bool', sub { 1; });
  52         172  
469 104     0   555 $PRO{keyword}($caller, '(eq', sub { $_[1]; });
  0         0  
470 104     0   601 $PRO{keyword}($caller, '((', sub { });
471             $PRO{keyword}($caller, '(%{}', sub {
472 1126     1126   74311 ${$_[0]}->{prototype};
  1126         10420  
473 104         451 });
474             $PRO{keyword}($caller, $_, $PRO{$_}($caller))
475 104 100       656 for $options->{import}
476 3         36 ? @{$options->{import}}
477             : qw/function property properties prototyped private readonly extends with requires before around after locked destroy DESTROY new/;
478             }
479              
480             sub new {
481 32     32 1 452207 my ($pkg, $meta, %params) = @_;
482              
483 32   66     157 my $name = $meta->{name} || 'Rope::Anonymous' . $META{ANONYMOUS}++;
484              
485 32 100       129 if (!$META{$name}) {
486 24         143 $META{$name} = {
487             name => $name,
488             locked => 0,
489             properties => {},
490             requires => {},
491             keys => 0
492             };
493            
494 24         48 my $use = 'use Rope;';
495 24         41 $use .= "use ${_};" for (@{$meta->{use}});
  24         93  
496              
497 24         138 my $c = sprintf(q|
498             package %s;
499             %s
500             1;
501             |, $name, $use);
502 24     10   2737 eval $c;
  10     6   88  
  10     5   17  
  10         79  
  6         51  
  6         13  
  6         24  
  5         2912  
  5         15  
  5         32  
503             }
504              
505 32         293 $pkg->set_meta($meta, $name);
506              
507 32 100       52 if (grep { $_ eq 'Rope::Monkey' } @{$meta->{use}}) {
  17         56  
  32         114  
508 8         80 $meta->{name}->monkey();
509             }
510              
511 32         159 return $PRO{new}($name)($name, %params);
512             }
513              
514             sub from_array {
515 12     12 1 164686 my ($pkg, $data, $meta) = @_;
516              
517 12   100     57 $meta ||= {};
518 12   66     79 $meta->{name} ||= 'Rope::Anonymous' . $META{ANONYMOUS}++;
519 12         20 my $len = scalar @{$data};
  12         25  
520 12         41 for (my $i = 0; $i < $len; $i++) {
521 51         114 my ($key, $value) = ($data->[$i++], $data->[$i]);
522 51         417 $meta->{properties}->{$key} = {
523             value => $value,
524             writeable => 1,
525             enumerable => 1
526             };
527             }
528              
529 12         38 return $pkg->new($meta)->new();
530             }
531              
532              
533             sub from_data {
534 12     12 1 180402 my ($pkg, $data, $meta) = @_;
535              
536 12   100     57 $meta ||= {};
537 12   66     67 $meta->{name} ||= 'Rope::Anonymous' . $META{ANONYMOUS}++;
538              
539 12         15 for (keys %{$data}) {
  12         41  
540             $meta->{properties}->{$_} = {
541 48         151 value => $data->{$_},
542             writeable => 1,
543             enumerable => 1
544             };
545             }
546              
547 12         36 return $pkg->new($meta)->new();
548             }
549              
550             sub from_nested_array {
551 9     9 1 196284 my ($pkg, $data, $meta) = @_;
552              
553 9         34 $data = $PRO{clone}($data);
554 9         23 for (my $i = 0; $i < scalar @{$data}; $i++) {
  45         141  
555 36         87 my ($key, $value) = ($data->[$i++], $data->[$i]);
556 36         57 my $ref = ref $value;
557 36 100       104 if ($ref eq 'ARRAY') {
558 9 100 100     66 if ((ref($value->[-1]) || "") eq 'HASH' && $value->[-1]->{ROPE_scope} eq 'ARRAY') {
    100 66        
559 3         8 pop @{$value};
  3         6  
560 3         15 next;
561             }
562             elsif (ref $value->[0]) {
563 3         8 for (my $x = 0; $x < scalar @{$value}; $x++) {
  6         45  
564 3         9 my ($val) = ($value->[$x]);
565 3         6 my $rref = ref($val); # || "";
566 3 50       10 if ($rref eq 'ARRAY') {
567 3         38 $val = $pkg->from_nested_array(
568             $val,
569             $meta
570             );
571 3         17 $value->[$x] = $val;
572             }
573             }
574             } else {
575 3         29 $data->[$i] = $pkg->from_nested_array($value, $meta);
576             }
577             }
578             }
579              
580 9         38 return $pkg->from_array($data, $meta);
581             }
582              
583             sub from_nested_data {
584 9     9 1 223657 my ($pkg, $data, $meta) = @_;
585              
586 9         24 $data = $PRO{clone}($data);
587              
588 9         18 for my $d (keys %{$data}) {
  9         23  
589 33         60 my $ref = ref $data->{$d};
590 33 100       116 if ($ref eq 'HASH') {
    100          
591 3         11 $data->{$d} = $pkg->from_nested_data($data->{$d}, $meta);
592             } elsif ($ref eq 'ARRAY') {
593 3         6 for (my $i = 0; $i < scalar @{$data->{$d}}; $i++) {
  6         23  
594 3         7 my $val = $data->{$d}->[$i];
595 3   50     11 my $rref = ref $val || "";
596 3 50       11 if ($rref eq 'HASH') {
597 3         10 $val = $pkg->from_nested_data(
598             $val,
599             $meta
600             );
601 3         13 $data->{$d}->[$i] = $val;
602             }
603             }
604             }
605             }
606              
607 9         32 return $pkg->from_data($data, $meta);
608             }
609              
610             sub get_initialised {
611 1     1 1 6 my ($self, $caller, $init) = @_;
612 1         6 return $META{initialised}{$caller}{$init};
613             }
614              
615             sub get_meta {
616 52     52 1 162 my ($self, $caller) = @_;
617 52   33     281 return $PRO{clone}($META{$caller || ref $self});
618             }
619              
620             sub set_meta {
621 33     33 1 1232 my ($self, $meta, $name) = @_;
622 33 100       105 $name = $meta->{name} if ! $name;
623 33 100       130 $META{$name}{locked} = $meta->{locked} if (defined $meta->{locked});
624 33 100       101 $PRO{requires}($name)(ref $meta->{requires} eq 'ARRAY' ? @{$meta->{requires}} : keys %{$meta->{requires}}) if ($meta->{requires});
  1 100       8  
  1         5  
625 33 100       120 $PRO{extends}($name)(@{$meta->{extends}}) if ($meta->{extends});
  1         4  
626 33 100       100 $PRO{with}($name)(@{$meta->{with}}) if ($meta->{with});
  1         8  
627 33 100       194 $PRO{properties}($name)(ref $meta->{properties} eq 'ARRAY' ? @{$meta->{properties}} : %{$meta->{properties}}) if ($meta->{properties});
  7 100       27  
  24         110  
628 33 100       352 $PRO{keyword}($name, 'INITIALISE', $meta->{INITIALISE}) if $meta->{INITIALISE};
629 33 100       130 $PRO{keyword}($name, 'INITIALISED', $meta->{INITIALISED}) if $meta->{INITIALISED};
630             }
631              
632             sub clear_meta {
633 1     1 1 12 my ($self, $name) = @_;
634 1         14 $META{$name} = {
635             name => $name,
636             locked => 0,
637             properties => {},
638             requires => {},
639             keys => 0
640             };
641             }
642              
643             sub clear_property {
644 5     5 0 16 my ($self, $name, $prop) = @_;
645 5         29 delete $META{$name}{properties}{$prop};
646             }
647              
648             sub set_property {
649 1     1 0 3 my ($self, $name, $prop, $property) = @_;
650 1         8 $META{$name}{properties}{$prop} = $property;
651             }
652              
653              
654             1;
655              
656             __END__