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.064';
4              
5 2     2   1335 use strict;
  2         2  
  2         74  
6 2     2   10 use warnings; no warnings 'utf8';
  2     2   3  
  2         82  
  2         12  
  2         4  
  2         83  
7              
8             # ~~~ delegate overloaded methods?
9              
10 2     2   9 use JE::Code 'add_line_number';
  2         2  
  2         111  
11 2     2   12 use Scalar::Util 1.09 qw'refaddr';
  2         53  
  2         2455  
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 53 my($class, $global, $obj) = @_;
33              
34 41         72 my $class_info = $$$global{classes}{ref $obj};
35              
36 41 100 33     412 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         320 @$$self{qw/class_info value/} = ($class_info, $obj);
43              
44 41         55 while(my($name,$args) = each %{$$class_info{props}}) {
  77         232  
45 36         134 $self->prop({ name => $name, @$args });
46             }
47              
48 41         248 $self;
49             }
50              
51              
52              
53              
54 12     12 1 420 sub class { $${$_[0]}{class_info}{name} }
  12         71  
55              
56              
57              
58              
59 125     125 1 111 sub value { $${$_[0]}{value} }
  125         480  
60              
61              
62              
63              
64             sub id {
65 34     34 0 33 refaddr $${$_[0]}{value};
  34         110  
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 41 my($self, $hint) = (shift, @_);
74              
75 22         30 my $guts = $$self;
76 22         30 my $value = $$guts{value};
77 22         29 my $class_info = $$guts{class_info};
78              
79 22 100       42 if(exists $$class_info{to_primitive}) {
80 10         12 my $tp = $$class_info{to_primitive};
81 10 100       18 if(defined $tp) {
82 6 100       77 ref $tp eq 'CODE' and
83             return $$guts{global}->upgrade(
84             &$tp($value, @_)
85             );
86 3         9 ($tp, my $type) = JE::_split_meth($tp);
87 3 100       36 return defined $type
88             ? $$guts{global}->_cast($value->$tp(@_),$type)
89             : $$guts{global}->upgrade($value->$tp(@_))
90             } else {
91 4         18 die add_line_number
92             "The object ($$class_info{name}) cannot "
93             . "be converted to a primitive";
94             }
95             } else {
96 12 50 33     39 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         3396 return SUPER::to_primitive $self @_;
102             }
103             }
104              
105              
106              
107             sub to_string {
108 18     18 0 113 my($self, $hint) = (shift, @_);
109              
110 18         24 my $guts = $$self;
111 18         32 my $value = $$guts{value};
112 18         23 my $class_info = $$guts{class_info};
113              
114 18 100       42 if(exists $$class_info{to_string}) {
115 5         8 my $tp = $$class_info{to_string};
116 5 100       10 if(defined $tp) {
117 4 100       22 ref $tp eq 'CODE' and
118             return $$guts{global}->upgrade(
119             &$tp($value, @_)
120             )->to_string;
121 1         5 ($tp, my $type) = JE::_split_meth $tp;
122 1 50       9 return ( defined $type
123             ? $$guts{global}->upgrade($value->$tp(@_))
124             : $$guts{global}->_cast($value->$tp(@_),$type)
125             )->to_string
126             } else {
127 1         10 die add_line_number
128             "The object ($$class_info{name}) cannot "
129             . "be converted to a string";
130             }
131             } else {
132 13         63 return SUPER::to_string $self @_;
133             }
134             }
135              
136              
137              
138              
139             sub to_number {
140 8     8 0 13 my($self, $hint) = (shift, @_);
141              
142 8         10 my $guts = $$self;
143 8         13 my $value = $$guts{value};
144 8         10 my $class_info = $$guts{class_info};
145              
146 8 100       19 if(exists $$class_info{to_number}) {
147 5         9 my $tp = $$class_info{to_number};
148 5 100       9 if(defined $tp) {
149 4 100       24 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       9 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         16 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.064'; # 'normal' usage
173             our @ISA = 'JE::Object::Proxy';
174             require JE::Number;
175              
176             sub prop {
177 30     30   33 my $self = shift;
178 30         44 my $wrappee = $self->value;
179 30         39 my $name = shift;
180 30         35 my $class_info = $$$self{class_info};
181              
182 30 100       60 if ($$class_info{array}) {
183 24 100       51 if($name eq 'length') {
184 9 100       40 @_ ? ($#$wrappee = $_[0]-1, return shift)
185             : return new JE::Number
186             $self->global, scalar @$wrappee
187             }
188 15 100 66     101 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     30 : do {
192 11         31 my $ret =
193             $$class_info{array}{fetch}(
194             $wrappee,$name);
195 11 100       55 defined $ret and return $ret;
196             }
197             }
198             }
199 13 100 66     71 if ($$class_info{hash}and !exists $$class_info{props}{$name}) {
200 10 100       22 if(@_){
201 2 50       8 $$class_info{hash}{store}->(
202             $wrappee,$name,$_[0]
203             ) and return shift;
204             }else{
205 8         21 my $ret = $$class_info{hash}{fetch}
206             ($wrappee,$name);
207 8 100       44 defined $ret and return $ret;
208             }
209             }
210 7         24 SUPER::prop $self $name, @_;
211             }
212              
213             sub keys {
214 32     32   37 my $self = shift;
215 32         51 my $wrappee = $self->value;
216 32         42 my $class_info = $$$self{class_info};
217 32         25 my @keys;
218 32 100       69 if ($$class_info{array}){
219 16         49 @keys = grep(exists $wrappee->[$_], 0..$#$wrappee);
220             }
221 32 100       77 if($$class_info{hash}) {
222 20         33 push @keys, keys %$wrappee;
223             }
224 32         98 push @keys, SUPER::keys $self;
225 32         33 my @new_keys; my %seen;
226 32   66     195 $seen{$_}++ or push @new_keys, $_ for @keys;
227 32         130 @new_keys;
228             }
229              
230             sub delete {
231 2     2   3 my $self = shift;
232 2         5 my $wrappee = $self->value;
233 2         5 my($name) = @_;
234 2         4 my $class_info = $$$self{class_info};
235 2 50       7 if ($$class_info{array}){
236 2 50 33     22 if ($name =~ /^(?:0|[1-9]\d*)\z/ and $name < 4294967295 and
    0 33        
237             exists $wrappee->[$name]) {
238 2         4 delete $wrappee->[$name];
239 2         7 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   11 my $self = shift;
255 4         10 my $wrappee = $self->value;
256 4         7 my($name) = @_;
257 4         9 my $class_info = $$$self{class_info};
258 4 100       11 if ($$class_info{array}){
259 2 50 33     14 if ($name =~ /^(?:0|[1-9]\d*)\z/ and $name < 4294967295) {
    0          
260 2 50       8 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       11 if($$class_info{hash}) {
268 2 50       13 return 1 if exists $wrappee->{$name};
269             }
270 2         15 SUPER::exists $self @_;
271              
272             }
273              
274              
275             1;
276