File Coverage

blib/lib/JE/Object/Proxy.pm
Criterion Covered Total %
statement 113 120 94.1
branch 57 72 79.1
condition 14 35 40.0
subroutine 16 16 100.0
pod 3 7 42.8
total 203 250 81.2


line stmt bran cond sub pod time code
1             package JE::Object::Proxy;
2              
3             our $VERSION = '0.065';
4              
5 2     2   1464 use strict;
  2         4  
  2         76  
6 2     2   10 use warnings; no warnings 'utf8';
  2     2   2  
  2         60  
  2         7  
  2         3  
  2         75  
7              
8             # ~~~ delegate overloaded methods?
9              
10 2     2   7 use JE::Code 'add_line_number';
  2         2  
  2         105  
11 2     2   9 use Scalar::Util 1.09 qw'refaddr';
  2         57  
  2         2475  
12              
13             require JE::Object;
14              
15             our @ISA = 'JE::Object';
16              
17              
18             =head1 NAME
19              
20             JE::Object::Proxy - JS wrapper for Perl objects
21              
22             =head1 SYNOPSIS
23              
24             $proxy = new JE::Object::Proxy $JE_object, $some_Perl_object;
25              
26             =cut
27              
28              
29              
30              
31             sub new {
32 41     41 1 51 my($class, $global, $obj) = @_;
33              
34 41         72 my $class_info = $$$global{classes}{ref $obj};
35              
36 41 100 33     384 my $self = ($class eq __PACKAGE__ # allow subclassing
37             && ($$class_info{hash} || $$class_info{array})
38             ? __PACKAGE__."::Array" : $class)
39             ->JE::Object::new($global,
40             { prototype => $$class_info{prototype} });
41              
42 41         293 @$$self{qw/class_info value/} = ($class_info, $obj);
43              
44 41         51 while(my($name,$args) = each %{$$class_info{props}}) {
  77         223  
45 36         119 $self->prop({ name => $name, @$args });
46             }
47              
48 41         315 $self;
49             }
50              
51              
52              
53              
54 12     12 1 299 sub class { $${$_[0]}{class_info}{name} }
  12         67  
55              
56              
57              
58              
59 125     125 1 113 sub value { $${$_[0]}{value} }
  125         439  
60              
61              
62              
63              
64             sub id {
65 34     34 0 29 refaddr $${$_[0]}{value};
  34         111  
66             }
67              
68              
69              
70              
71             sub to_primitive { # ~~~ This code should probably be moved to
72             # &JE::bind_class for the sake of efficiency.
73 22     22 0 36 my($self, $hint) = (shift, @_);
74              
75 22         25 my $guts = $$self;
76 22         25 my $value = $$guts{value};
77 22         23 my $class_info = $$guts{class_info};
78              
79 22 100       40 if(exists $$class_info{to_primitive}) {
80 10         9 my $tp = $$class_info{to_primitive};
81 10 100       17 if(defined $tp) {
82 6 100       36 ref $tp eq 'CODE' and
83             return $$guts{global}->upgrade(
84             &$tp($value, @_)
85             );
86 3         8 ($tp, my $type) = JE::_split_meth($tp);
87 3 100       35 return defined $type
88             ? $$guts{global}->_cast($value->$tp(@_),$type)
89             : $$guts{global}->upgrade($value->$tp(@_))
90             } else {
91 4         17 die add_line_number
92             "The object ($$class_info{name}) cannot "
93             . "be converted to a primitive";
94             }
95             } else {
96 12 50 33     37 if(overload::Method($value,'""') ||
      33        
97             overload::Method($value,'0+') ||
98             overload::Method($value,'bool')){
99 0         0 return $$guts{global}->upgrade("$value");
100             }
101 12         3417 return SUPER::to_primitive $self @_;
102             }
103             }
104              
105              
106              
107             sub to_string {
108 18     18 0 124 my($self, $hint) = (shift, @_);
109              
110 18         26 my $guts = $$self;
111 18         25 my $value = $$guts{value};
112 18         20 my $class_info = $$guts{class_info};
113              
114 18 100       33 if(exists $$class_info{to_string}) {
115 5         8 my $tp = $$class_info{to_string};
116 5 100       9 if(defined $tp) {
117 4 100       15 ref $tp eq 'CODE' and
118             return $$guts{global}->upgrade(
119             &$tp($value, @_)
120             )->to_string;
121 1         4 ($tp, my $type) = JE::_split_meth $tp;
122 1 50       7 return ( defined $type
123             ? $$guts{global}->upgrade($value->$tp(@_))
124             : $$guts{global}->_cast($value->$tp(@_),$type)
125             )->to_string
126             } else {
127 1         8 die add_line_number
128             "The object ($$class_info{name}) cannot "
129             . "be converted to a string";
130             }
131             } else {
132 13         51 return SUPER::to_string $self @_;
133             }
134             }
135              
136              
137              
138              
139             sub to_number {
140 8     8 0 12 my($self, $hint) = (shift, @_);
141              
142 8         9 my $guts = $$self;
143 8         12 my $value = $$guts{value};
144 8         9 my $class_info = $$guts{class_info};
145              
146 8 100       16 if(exists $$class_info{to_number}) {
147 5         7 my $tp = $$class_info{to_number};
148 5 100       9 if(defined $tp) {
149 4 100       17 ref $tp eq 'CODE' and
150             return $$guts{global}->upgrade(
151             &$tp($value, @_)
152             )->to_number;
153 1         4 ($tp, my $type) = JE::_split_meth $tp;
154 1 50       7 return ( defined $type
155             ? $$guts{global}->upgrade($value->$tp(@_))
156             : $$guts{global}->_cast($value->$tp(@_),$type)
157             )->to_number
158             } else {
159 1         7 die add_line_number
160             "The object ($$class_info{name}) cannot "
161             . "be converted to a number";
162             }
163             } else {
164 3         12 return SUPER::to_number $self @_;
165             }
166             }
167              
168              
169              
170              
171             package JE::Object::Proxy::Array; # so this extra stuff doesn't slow down
172             our $VERSION = '0.065'; # 'normal' usage
173             our @ISA = 'JE::Object::Proxy';
174             require JE::Number;
175              
176             sub prop {
177 30     30   32 my $self = shift;
178 30         49 my $wrappee = $self->value;
179 30         32 my $name = shift;
180 30         35 my $class_info = $$$self{class_info};
181              
182 30 100       58 if ($$class_info{array}) {
183 24 100       45 if($name eq 'length') {
184 9 100       35 @_ ? ($#$wrappee = $_[0]-1, return shift)
185             : return new JE::Number
186             $self->global, scalar @$wrappee
187             }
188 15 100 66     98 if($name =~ /^(?:0|[1-9]\d*)\z/ and $name < 4294967295){
189             @_ ? $$class_info{array}{store}(
190             $wrappee,$name,$_[0]) && return shift
191 13 100 100     27 : do {
192 11         29 my $ret =
193             $$class_info{array}{fetch}(
194             $wrappee,$name);
195 11 100       51 defined $ret and return $ret;
196             }
197             }
198             }
199 13 100 66     54 if ($$class_info{hash}and !exists $$class_info{props}{$name}) {
200 10 100       16 if(@_){
201 2 50       6 $$class_info{hash}{store}->(
202             $wrappee,$name,$_[0]
203             ) and return shift;
204             }else{
205 8         22 my $ret = $$class_info{hash}{fetch}
206             ($wrappee,$name);
207 8 100       38 defined $ret and return $ret;
208             }
209             }
210 7         22 SUPER::prop $self $name, @_;
211             }
212              
213             sub keys {
214 32     32   32 my $self = shift;
215 32         44 my $wrappee = $self->value;
216 32         40 my $class_info = $$$self{class_info};
217 32         23 my @keys;
218 32 100       55 if ($$class_info{array}){
219 16         41 @keys = grep(exists $wrappee->[$_], 0..$#$wrappee);
220             }
221 32 100       77 if($$class_info{hash}) {
222 20         30 push @keys, keys %$wrappee;
223             }
224 32         85 push @keys, SUPER::keys $self;
225 32         36 my @new_keys; my %seen;
226 32   66     189 $seen{$_}++ or push @new_keys, $_ for @keys;
227 32         123 @new_keys;
228             }
229              
230             sub delete {
231 2     2   3 my $self = shift;
232 2         3 my $wrappee = $self->value;
233 2         3 my($name) = @_;
234 2         4 my $class_info = $$$self{class_info};
235 2 50       6 if ($$class_info{array}){
236 2 50 33     19 if ($name =~ /^(?:0|[1-9]\d*)\z/ and $name < 4294967295 and
    0 33        
237             exists $wrappee->[$name]) {
238 2         3 delete $wrappee->[$name];
239 2         5 return !$self->exists($name);
240             }
241             elsif ($name eq 'length') {
242 0         0 return !1
243             }
244             }
245 0 0 0     0 if($$class_info{hash} && !exists $$class_info{props}{$name} and
      0        
246             exists $wrappee->{$name}) {
247 0         0 delete $wrappee->{$name};
248 0         0 return !exists $wrappee->{$name};
249             }
250 0         0 SUPER::delete $self @_;
251             }
252              
253             sub exists {
254 4     4   10 my $self = shift;
255 4         6 my $wrappee = $self->value;
256 4         5 my($name) = @_;
257 4         5 my $class_info = $$$self{class_info};
258 4 100       9 if ($$class_info{array}){
259 2 50 33     10 if ($name =~ /^(?:0|[1-9]\d*)\z/ and $name < 4294967295) {
    0          
260 2 50       5 return 1 if exists $wrappee->[$name];
261             # If it doesn’t exists, try hash keys below.
262             }
263             elsif ($name eq 'length') {
264 0         0 return 1
265             }
266             }
267 4 100       8 if($$class_info{hash}) {
268 2 50       12 return 1 if exists $wrappee->{$name};
269             }
270 2         10 SUPER::exists $self @_;
271              
272             }
273              
274              
275             1;
276