File Coverage

blib/lib/Rope.pm
Criterion Covered Total %
statement 91 110 82.7
branch 14 32 43.7
condition 3 11 27.2
subroutine 13 14 92.8
pod n/a
total 121 167 72.4


line stmt bran cond sub pod time code
1             package Rope;
2              
3 5     5   379076 use 5.006; use strict; use warnings;
  5     5   49  
  5     5   25  
  5         9  
  5         121  
  5         23  
  5         10  
  5         254  
4             our $VERSION = '0.03';
5              
6 5     5   2133 use Rope::Object;
  5         12  
  5         348  
7             my (%META);
8             our (%PRO);
9             BEGIN {
10             %PRO = (
11             keyword => sub {
12 49         91 my ($caller, $method, $cb) = @_;
13 5     5   32 no strict 'refs';
  5         9  
  5         6527  
14 49         63 *{"${caller}::${method}"} = $cb;
  49         3571  
15             },
16             scope => sub {
17 6         28 my ($self, %props) = @_;
18 6         11 for (keys %{$props{properties}}) {
  6         25  
19 23         33 $props{properties}->{$_} = {%{$props{properties}{$_}}};
  23         84  
20 23 100 100     142 if ($props{properties}{$_}{value} && ref $props{properties}{$_}{value} eq 'CODE') {
21 13         22 my $cb = $props{properties}{$_}{value};
22 13         57 $props{properties}{$_}{value} = sub { $cb->($self, @_) };
  11         45  
23             }
24             }
25 6         43 return \%props;
26             },
27             clone => sub {
28 38         47 my $obj = shift;
29 38         46 my $ref = ref $obj;
30 38 100       99 return $obj if !$ref;
31 12 50       21 return [ map { $PRO{clone}->($_) } @{$obj} ] if $ref eq 'ARRAY';
  0         0  
  0         0  
32 12 100       33 return { map { $_ => $PRO{clone}->($obj->{$_}) } keys %{$obj} } if $ref eq 'HASH';
  36         65  
  8         21  
33 4         16 return $obj;
34             },
35             set_prop => sub {
36 18         63 my ($caller, $prop, %options) = @_;
37 18 50       45 if ($META{$caller}{properties}{$prop}) {
38 0 0       0 if ($META{$caller}{properties}{$prop}{writeable}) {
    0          
39 0         0 $META{$caller}{properties}{$prop}{value} = $options{value};
40 0         0 $META{$caller}{properties}{$prop}{class} = $caller;
41             } elsif ($META{$caller}{properties}{$prop}{configurable}) {
42 0 0 0     0 if ((ref($META{$caller}{properties}{$prop}{value}) || "") eq (ref($options{value}) || "")) {
      0        
43 0         0 $META{$caller}{properties}{$prop}{value} = $options{value};
44 0         0 $META{$caller}{properties}{$prop}{class} = $caller;
45             } else {
46 0         0 die "Cannot inherit $META{$caller}{properties}{$prop}{class} and change property $prop type";
47             }
48             } else {
49 0         0 die "Cannot inherit $META{$caller}{properties}{$prop}{class} and change property $prop type";
50             }
51             } else {
52             $META{$caller}{properties}{$prop} = {
53             %options,
54             class => $caller,
55             index => ++$META{$caller}{keys}
56 18         121 };
57             }
58             },
59             function => sub {
60 7         17 my ($caller) = shift;
61             return sub {
62 2     2   16 my ($prop, @options) = @_;
63 2 50       8 $prop = shift @options if ( @options > 1 );
64             $PRO{set_prop}(
65 2         12 $caller,
66             $prop,
67             value => $options[0],
68             enumerable => 0,
69             writeable => 0,
70             configurable => 1
71             );
72 7         40 };
73             },
74             property => sub {
75 7         15 my ($caller) = shift;
76             return sub {
77 9     9   31 my ($prop, @options) = @_;
78 9 100       24 if (scalar @options % 2) {
79 6         8 $prop = shift @options;
80             }
81             $PRO{set_prop}(
82 9         22 $caller,
83             $prop,
84             @options
85             );
86 7         23 };
87             },
88             prototyped => sub {
89 7         12 my ($caller) = shift;
90             return sub {
91 5     5   200 my (@proto) = @_;
92 5         17 while (@proto) {
93 7         19 my ($prop, $value) = (shift @proto, shift @proto);
94             $PRO{set_prop}(
95 7         21 $caller,
96             $prop,
97             enumerable => 1,
98             writeable => 1,
99             configurable => 1,
100             value => $value
101             );
102             }
103             }
104 7         23 },
105             extends => sub {
106 7         15 my ($caller) = shift;
107             return sub {
108 2     2   16 my (@extends) = @_;
109 2         6 for my $extend (@extends) {
110 2 100       9 if (!$META{$extend}) {
111 1         3 (my $name = $extend) =~ s!::!/!g;
112 1         2 $name .= ".pm";
113 1         309 CORE::require($name);
114             }
115 2         12 my $initial = $META{$caller};
116 2         8 my $merge = $PRO{clone}($META{$extend});
117 2         5 $merge->{name} = $initial->{name};
118 2         4 $merge->{locked} = $initial->{locked};
119 2         4 for (keys %{$initial->{properties}}) {
  2         8  
120 0         0 $initial->{properties}->{$_}->{index} = ++$merge->{keys};
121 0 0       0 if ($merge->{properties}->{$_}) {
122 0 0       0 if ($merge->{properties}->{writeable}) {
    0          
123 0         0 $merge->{properties}->{$_} = $initial->{properties}->{$_};
124             } elsif ($merge->{properties}->{configurable}) {
125 0 0 0     0 if ((ref($merge->{properties}->{$_}->{value}) || "") eq (ref($initial->{properties}->{$_}->{value} || ""))) {
      0        
126 0         0 $merge->{properties}->{$_} = $initial->{properties}->{$_};
127             } else {
128 0         0 die "Cannot inherit $extend and change property $_ type";
129             }
130             } else {
131 0         0 die "Cannot inherit $extend and override property $_";
132             }
133             } else {
134 0         0 $merge->{properties}->{$_} = $initial->{properties}->{$_};
135             }
136             }
137 2         9 $META{$caller} = $merge;
138             }
139             }
140 7         34 },
141             new => sub {
142 7         18 my ($caller) = shift;
143             return sub {
144 6     6   1165 my ($class, %params) = @_;
145 6         20 my $self = \{
146             prototype => {},
147             };
148 6         18 $self = bless $self, $caller;
149 6         8 tie %{${$self}->{prototype}}, 'Rope::Object', $PRO{scope}($self, %{$META{$caller}});
  6         11  
  6         148  
  6         36  
150 6         22 for (keys %params) {
151 4         11 $self->{$_} = $params{$_};
152             }
153 6         18 return $self;
154 7         22 };
155             }
156 5     5   1158 );
157             }
158              
159             sub import {
160 7     7   88 my ($pkg, $options, $caller) = (shift, {}, caller());
161 7 50       27 if (!$META{$caller}) {
162 7         30 $META{$caller} = {
163             name => $caller,
164             locked => 0,
165             properties => {},
166             keys => 0
167             };
168             }
169 7     0   37 $PRO{keyword}($caller, '((', sub {});
170             $PRO{keyword}($caller, '(%{}', sub {
171 38     38   5299 ${$_[0]}->{prototype};
  38         240  
172 7         31 });
173             $PRO{keyword}($caller, $_, $PRO{$_}($caller))
174 7         31 for qw/function property prototyped extends new/;
175             }
176              
177             1;
178              
179             __END__