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