File Coverage

blib/lib/Rope.pm
Criterion Covered Total %
statement 90 110 81.8
branch 13 32 40.6
condition 2 11 18.1
subroutine 13 14 92.8
pod n/a
total 118 167 70.6


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