File Coverage

blib/lib/Data/JavaScript/Anon.pm
Criterion Covered Total %
statement 85 128 66.4
branch 27 60 45.0
condition 7 18 38.8
subroutine 14 23 60.8
pod 12 12 100.0
total 145 241 60.1


line stmt bran cond sub pod time code
1             package Data::JavaScript::Anon;
2              
3             # This package provides a mechanism to convert the main basic Perl
4             # structures into JavaScript structures, making it easier to transfer
5             # data from Perl to JavaScript.
6              
7 2     2   30070 use 5.006;
  2         9  
  2         72  
8 2     2   12 use strict;
  2         4  
  2         70  
9 2     2   21 use warnings;
  2         4  
  2         74  
10 2     2   1965 use Params::Util qw{ _STRING _SCALAR0 _ARRAY0 _HASH0 };
  2         9801  
  2         179  
11 2     2   1875 use Class::Default ();
  2         750  
  2         45  
12              
13 2     2   12 use vars qw{@ISA $VERSION $errstr $RE_NUMERIC $RE_NUMERIC_HASHKEY %KEYWORD};
  2         4  
  2         670  
14             BEGIN {
15 2     2   5 $VERSION = '1.03';
16 2         30 @ISA = 'Class::Default';
17 2         4 $errstr = '';
18              
19             # Attempt to define a single, all encompasing,
20             # regex for detecting a legal JavaScript number.
21             # We do not support the exotic values, such as Infinite and NaN.
22 2         9 my $_sci = qr/[eE](?:\+|\-)?\d+/; # The scientific notation exponent ( e.g. 'e+12' )
23 2         5 my $_dec = qr/\.\d+/; # The decimal section ( e.g. '.0212' )
24 2         11 my $_int = qr/(?:[1-9]\d*|0)/; # The integers section ( e.g. '2312' )
25 2         94 my $real = qr/(?:$_int(?:$_dec)?|$_dec)(?:$_sci)?/; # Merge the integer, decimal and scientific parts
26 2         10 my $_hex = qr/0[xX][0-9a-fA-F]+/; # Hexidecimal notation
27 2         5 my $_oct = qr/0[0-7]+/; # Octal notation
28              
29             # The final combination of all posibilities for a straight number
30             # The string to match must have no extra characters
31 2         155 $RE_NUMERIC = qr/^(?:\+|\-)??(?:$real|$_hex|$_oct)\z/;
32              
33             # The numeric for of the hash key is similar, but without the + or - allowed
34 2         111 $RE_NUMERIC_HASHKEY = qr/^(?:$real|$_hex|$_oct)\z/;
35              
36 2         8 %KEYWORD = map { $_ => 1 } qw{
  118         3494  
37             abstract boolean break byte case catch char class const
38             continue debugger default delete do double else enum export
39             extends false final finally float for function goto if
40             implements import in instanceof int interface long native new
41             null package private protected public return short static super
42             switch synchronized this throw throws transient true try typeof
43             var void volatile while with
44             };
45             }
46              
47              
48              
49              
50              
51             #####################################################################
52             # Top Level Dumping Methods
53              
54             sub new {
55 2     2 1 459 my $proto = shift;
56 2   33     19 my $class = ref $proto || $proto;
57 2 50       20 my $opts = _HASH0($_[0]) ? shift : { @_ };
58              
59             # Create the object
60 2         12 my $self = bless {
61             quote_char => '"',
62             }, $class;
63              
64             ## change the default quote character
65 2 100 66     10 if ( defined $opts->{quote_char} && length $opts->{quote_char} ) {
66 1         2 $self->{quote_char} = $opts->{quote_char};
67             }
68              
69 2         5 return $self;
70             }
71              
72             sub _create_default_object {
73 1     1   8 my $class = shift;
74 1         4 my $self = $class->new();
75 1         3 return $self;
76             }
77              
78             sub anon_dump {
79 19     19 1 868 my $class = shift;
80 19         16 my $something = shift;
81 19   100     44 my $processed = shift || {};
82              
83             # Handle the undefined case
84 19 50       36 return 'undefined' unless defined $something;
85              
86             # Handle the basic non-reference case
87 19 100       47 return $class->anon_scalar( $something ) unless ref $something;
88              
89             # Check to see if we have processed this reference before.
90             # This should catch circular, cross-linked, or otherwise complex things
91             # that we can't handle.
92 6 50       13 if ( $processed->{$something} ) {
93 0         0 return $class->_err_found_twice( $something );
94             } else {
95 6         60 $processed->{$something} = 1;
96             }
97              
98             # Handle the SCALAR reference case, which in our case we treat
99             # like a normal scalar.
100 6 100       17 if ( _SCALAR0($something) ) {
101 1         3 return $class->anon_scalar( $something );
102             }
103              
104             # Handle the array case by generating an anonymous array
105 5 100       13 if ( _ARRAY0($something) ) {
106             # Create and return the array
107 3         14 my $list = join ', ', map { $class->anon_dump($_, $processed) } @$something;
  14         33  
108 3         14 return "[ $list ]";
109             }
110              
111             # Handle the hash case by generating an anonymous object/hash
112 2 50       7 if ( _HASH0($something) ) {
113             # Create and return the anonymous hash
114 2         5 my $pairs = join ', ', map {
115 2         5 $class->anon_hash_key($_)
116             . ': '
117             . $class->anon_dump( $something->{$_}, $processed )
118             } keys %$something;
119 2         7 return "{ $pairs }";
120             }
121              
122 0         0 $class->_err_not_supported( $something );
123             }
124              
125             # Same thing, but creating a variable
126             sub var_dump {
127 0     0 1 0 my $class = shift;
128 0 0       0 my $name = shift or return undef;
129 0         0 my $value = $class->anon_dump( shift );
130 0         0 "var $name = $value;";
131             }
132              
133             # Wrap some JavaScript in a HTML script tag
134             sub script_wrap {
135 0     0 1 0 "";
136             }
137              
138             # Is a particular string a legal JavaScript number.
139             # Returns true if a legal JavaScript number.
140             # Returns false otherwise.
141             sub is_a_number {
142 74     74 1 25645 my $class = shift;
143 74 50 33     330 my $number = (defined $_[0] and ! ref $_[0]) ? shift : '';
144 74 100       688 $number =~ m/$RE_NUMERIC/ ? 1 : '';
145             }
146              
147              
148              
149              
150              
151             #####################################################################
152             # Basic Variable Creation Statements
153              
154             # Create a JavaScript scalar given the javascript variable name
155             # and a reference to the scalar.
156             sub var_scalar {
157 0     0 1 0 my $class = shift;
158 0 0       0 my $name = shift or return undef;
159 0 0       0 my $scalar_ref = _SCALAR0(shift) or return undef;
160 0 0       0 my $value = $class->js_value( $$scalar_ref ) or return undef;
161 0         0 "var $name = $value;";
162             }
163              
164             # Create a JavaScript array given the javascript array name
165             # and a reference to the array.
166             sub var_array {
167 0     0 1 0 my $class = shift;
168 0 0       0 my $name = shift or return undef;
169 0 0       0 my $array_ref = _ARRAY0(shift) or return undef;
170 0         0 my $list = join ', ', map { $class->anon_dump($_) } @$array_ref;
  0         0  
171 0         0 "var $name = new Array( $list );";
172             }
173              
174             # Create a JavaScript hash ( which is just an object ), given
175             # the variable name, and a reference to a hash.
176             sub var_hash {
177 0     0 1 0 my $class = shift;
178 0 0       0 my $name = shift or return undef;
179 0 0       0 my $hash_ref = _HASH0(shift) or return undef;
180 0 0       0 my $struct = $class->anon_hash( $name, $hash_ref ) or return undef;
181 0         0 "var $name = $struct;";
182             }
183              
184              
185              
186              
187              
188             #####################################################################
189             # Basic Serialisation And Escaping Methods
190              
191             # Turn a single perl value into a single javascript value
192             sub anon_scalar {
193 16     16 1 614 my $class = shift;
194 16 100       39 my $value = _SCALAR0($_[0]) ? ${shift()} : shift;
  1         2  
195 16 50       27 return 'null' unless defined $value;
196              
197             # Don't quote if it is numeric
198 16 100       89 return $value if $value =~ /$RE_NUMERIC/;
199              
200 14         45 my $quote_char = $class->_self->{quote_char};
201              
202             # Escape and quote
203 14         83 $quote_char . $class->_escape($value) . $quote_char;
204             }
205              
206             # Turn a single perl value into a javascript hash key
207             sub anon_hash_key {
208 66     66 1 31578 my $class = shift;
209 66 50 33     323 my $value = defined($_[0]) && !ref($_[0]) ? shift : return undef;
210              
211 66         205 my $quote_char = $class->_self->{quote_char};
212              
213             # Quote if it's a keyword
214 66 100       690 return $quote_char . $value . $quote_char if $KEYWORD{$value};
215              
216             # Don't quote if it is just a set of word characters or numeric
217 7 100       32 return $value if $value =~ /^[^\W\d]\w*\z/;
218 5 100       32 return $value if $value =~ /$RE_NUMERIC_HASHKEY/;
219              
220             # Escape and quote
221 4         12 $quote_char . $class->_escape($value) . $quote_char;
222             }
223              
224             # Create a JavaScript array given the javascript array name
225             # and a reference to the array.
226             sub anon_array {
227 0     0 1 0 my $class = shift;
228 0 0       0 my $name = shift or return undef;
229 0 0       0 my $array_ref = _ARRAY0(shift) or return undef;
230 0         0 my $list = join ', ', map { $class->anon_scalar($_) } @$array_ref;
  0         0  
231 0         0 "[ $list ]";
232             }
233              
234             # Create a JavaScript hash ( which is just an object ), given
235             # the variable name, and a reference to a hash.
236             sub anon_hash {
237 0     0 1 0 my $class = shift;
238 0 0       0 my $name = shift or return undef;
239 0 0       0 my $hash_ref = _HASH0(shift) or return undef;
240 0         0 my $pairs = join ', ', map {
241 0         0 $class->anon_hash_key( $_ )
242             . ': '
243             . $class->anon_scalar( $hash_ref->{$_} )
244             } keys %$hash_ref;
245 0         0 "{ $pairs }";
246             }
247              
248              
249              
250              
251              
252             #####################################################################
253             # Utility and Error Methods
254              
255             sub _escape {
256 18     18   18 my $class = shift;
257 18         19 my $text = shift;
258 18         42 my $char = $class->_self->{quote_char};
259 18         219 $text =~ s/(\Q$char\E|\\)/\\$1/g; # Escape quotes and backslashes
260 18         31 $text =~ s/\n/\\n/g; # Escape newlines in a readable way
261 18         18 $text =~ s/\r/\\r/g; # Escape CRs in a readable way
262 18         20 $text =~ s/\t/\\t/g; # Escape tabs in a readable way
263 18         78 $text =~ s/([\x00-\x1F])/sprintf("\\%03o", ord($1))/ge; # Escape other control chars as octal
  2         17  
264 18         128 $text;
265             }
266              
267             sub _err_found_twice {
268 0     0     my $class = shift;
269 0   0       my $something = ref $_[0] || 'a reference';
270 0           $errstr = "Found $something in your dump more than once. "
271             . "Data::JavaScript::Anon does not support complex, "
272             . "circular, or cross-linked data structures";
273 0           undef;
274             }
275              
276             sub _err_not_supported {
277 0     0     my $class = shift;
278 0   0       my $something = ref $_[0] || 'A reference of unknown type';
279 0           $errstr = "$something was found in the dump struct. "
280             . "Data::JavaScript::Anon only supports objects based on, "
281             . "or references to SCALAR, ARRAY and HASH type variables.";
282 0           undef;
283             }
284              
285             1;
286              
287             __END__