File Coverage

blib/lib/JSON/T.pm
Criterion Covered Total %
statement 32 86 37.2
branch 3 16 18.7
condition 1 13 7.6
subroutine 9 22 40.9
pod 9 9 100.0
total 54 146 36.9


line stmt bran cond sub pod time code
1 1     1   13758 use 5.010;
  1         2  
  1         30  
2 1     1   3 use strict;
  1         1  
  1         21  
3 1     1   3 use warnings;
  1         4  
  1         25  
4 1     1   507 use utf8;
  1         7  
  1         3  
5              
6             package JSON::T;
7              
8 1     1   1010 use overload '""' => \&_to_string;
  1         730  
  1         5  
9              
10             BEGIN
11             {
12 1     1   50 $JSON::T::AUTHORITY = 'cpan:TOBYINK';
13 1         69 $JSON::T::VERSION = '0.104';
14             }
15              
16             our ($JSLIB, @Implementations);
17              
18             sub _load_lib
19             {
20 0 0   0   0 unless ($JSLIB)
21             {
22 0         0 local $/ = undef;
23 0         0 $JSLIB = ;
24             }
25             }
26              
27             BEGIN
28             {
29 1     1   15 push @Implementations, qw/
30             JSON::T::SpiderMonkey
31             JSON::T::JE
32             /;
33             }
34              
35             {
36 1     1   4 no warnings 'redefine';
  1         1  
  1         657  
37             sub DOES
38             {
39 0     0 1 0 my ($class, $role) = @_;
40 0 0       0 return $role if $role eq 'XML::Saxon::XSLT2';
41 0         0 return $class->SUPER::DOES($role);
42             }
43             }
44              
45             sub new
46             {
47 1     1 1 11 my $class = shift;
48 1         2 my ($transformation_code, $transformation_name) = @_;
49 1   50     7 $transformation_name ||= '_main';
50            
51 1 50       3 if ($class eq __PACKAGE__)
52             {
53 1         608 require Module::Runtime;
54 1         1454 IMPL: for my $subclass (@Implementations)
55             {
56 2 50       2 next IMPL unless eval { Module::Runtime::use_module($subclass) };
  2         6  
57 0         0 $class = $subclass;
58 0         0 last IMPL;
59             }
60             }
61            
62 1 50       4 if ($class eq __PACKAGE__)
63             {
64 1         3 require Carp;
65 1         229 Carp::croak("cannot load any known Javascript engine");
66             }
67            
68 0           my $self = bless {
69             code => $transformation_code ,
70             name => $transformation_name ,
71             messages => [],
72             }, $class;
73            
74 0           $self->init;
75 0           $self->engine_eval($transformation_code);
76            
77 0           $self;
78             }
79              
80             sub init
81             {
82 0     0 1   my $self = shift;
83            
84 0           _load_lib;
85 0           $self->engine_eval($JSLIB);
86            
87 0           $self;
88             }
89              
90 0     0 1   sub engine_eval { require Carp; Carp::croak("must be implemented by subclass") }
  0            
91 0     0 1   sub parameters { require Carp; Carp::carp("not implemented by subclass") }
  0            
92              
93             sub _accept_return_value
94             {
95 0     0     my $self = shift;
96 0           my ($value) = @_;
97            
98 0           $self->{return_value} = $value;
99             }
100              
101             sub _last_return_value
102             {
103 0     0     my $self = shift;
104            
105 0           $self->{return_value};
106             }
107              
108             sub _to_string
109             {
110 0     0     my $self = shift;
111            
112 0           return 'JsonT:#'.$self->{'name'};
113             }
114              
115             sub _json_backend
116             {
117 0     0     my $self = shift;
118            
119 0   0       $self->{'json_backend'} ||= eval {
120 0           require Cpanel::JSON::MaybeXS;
121 0           'Cpanel::JSON::MaybeXS';
122             };
123 0   0       $self->{'json_backend'} ||= do {
124 0           require JSON::PP;
125 0           'JSON::PP';
126             };
127            
128 0           $self->{'json_backend'};
129             }
130              
131             sub transform
132             {
133 0     0 1   my $self = shift;
134 0           my ($input) = @_;
135            
136 0 0         if (ref $input)
137             {
138 0           require Scalar::Util;
139 0 0 0       if (Scalar::Util::blessed($input) and $input->isa('JSON::JOM::Node'))
140             {
141 0           $input = $self->_json_backend->new->convert_blessed(1)->encode($input);
142             }
143             else
144             {
145 0           $input = $self->_json_backend->new->encode($input);
146             }
147             }
148            
149 0           my $name = $self->{'name'};
150 0           my $rv1 = $self->engine_eval("return_to_perl(JSON.transform($input, $name));");
151              
152 0   0       ($self->_last_return_value // '') . ''; # stringify
153             }
154              
155             sub transform_structure
156             {
157 0     0 1   my $self = shift;
158 0           my ($input, $debug) = @_;
159            
160 0           my $output = $self->transform($input);
161 0 0         eval 'use Test::More; Test::More::diag("\n${output}\n");'
162             if $debug;
163            
164 0           $self->_json_backend->new->decode($output);
165             }
166              
167             *transform_document = \&transform_structure;
168              
169             # none of this is useful, but provided for XML::Saxon::XSLT2 compat.
170             sub messages
171             {
172 0     0 1   return;
173             }
174              
175             sub media_type
176             {
177 0     0 1   my $self = shift;
178 0           my ($default) = @_;
179            
180 0           $default;
181             }
182              
183             *version = \&media_type;
184             *doctype_system = \&media_type;
185             *doctype_public = \&media_type;
186             *encoding = \&media_type;
187              
188             1;
189              
190             #
191             # Don't include __END__ here because we
192             # have a __DATA__ section below the pod!
193             #
194              
195             =pod
196              
197             =encoding utf-8
198              
199             =head1 NAME
200              
201             JSON::T - transform JSON using JsonT
202              
203             =head1 SYNOPSIS
204              
205             my $jsont = slurp('foo/bar.js');
206             my $input = slurp('foo/quux.json');
207             my $JSONT = JSON::T->new($jsont);
208             print $JSONT->transform($input);
209              
210             =head1 DESCRIPTION
211              
212             This module implements JsonT, a language for transforming JSON-like
213             structures, analogous to XSLT in the XML world.
214              
215             JsonT is described at L. JsonT is
216             a profile of Javascript; so JsonT needs a Javascript engine to actually
217             work. This module provides the engine-neutral stuff, while L
218             and L provide the necessary glue to hook it up to
219             a couple of Javascript engines.
220              
221             JSON::T::JE uses the pure Perl Javascript implementation L.
222              
223             JSON::T::SpiderMonkey uses L which in turn is
224             backed by Mozilla's libjs C library.
225              
226             This module tries to provide a similar API to L.
227              
228             =head2 Constructor
229              
230             =over 4
231              
232             =item C<< new($code, $name) >>
233              
234             Constructs a new JSON::T transformation. $code is the JsonT Javascript
235             code. As a JsonT file can contain multiple (potentially unrelated)
236             transformations, the name of the particular transformation you want to
237             use should also be provided. If $name is omitted, then the name "_main"
238             is assumed.
239              
240             If you wish to use a particular Javascript implementation, you can
241             use, for example:
242              
243             JSON::T::SpiderMonkey->new($code, $name)
244              
245             Otherwise
246              
247             JSON::T->new($code, $name)
248              
249             will try to pick a working implementation for you.
250              
251             =back
252              
253             =head2 Methods
254              
255             =over 4
256              
257             =item C<< parameters(param1=>$arg1, param2=>$arg2, ...) >>
258              
259             Sets global variables available to the Javascript code. All arguments
260             are treated as strings.
261              
262             =item C<< transform($input) >>
263              
264             Run the transformation. The input may be a JSON string, a JSON::JOM::Node
265             or a native Perl nested arrayref/hashref structure, in which case it will be
266             stringified using the JSON module's to_json function. The output (return value)
267             will be a string.
268              
269             =item C<< transform_structure($input) >>
270              
271             Like C, but attempts to parse the output as a JSON string and
272             return a native Perl arrayref/hashref structure. This method will fail
273             if the output is not a JSON string.
274              
275             =item C<< DOES($role) >>
276              
277             Like L's DOES method, but returns true for:
278              
279             JSON::T->DOES('XML::Saxon::XSLT2')
280              
281             as an aid for polymorphism.
282              
283             =back
284              
285             The following methods also exist for compatibility with XML::Saxon::XSLT2,
286             but are mostly useless:
287              
288             =over
289              
290             =item C<< transform_document >>
291              
292             =item C<< messages >>
293              
294             =item C<< media_type >>
295              
296             =item C<< version >>
297              
298             =item C<< doctype_system >>
299              
300             =item C<< doctype_public >>
301              
302             =item C<< encoding >>
303              
304             =back
305              
306             =head2 Javascript Execution Environment
307              
308             JSON::T is a profile of Javascript, so is evaluated in an execution
309             environment. As this is not a browser environment, many global objects
310             familiar to browser Javascript developers are not available. (For example,
311             C, C, C, etc.)
312              
313             A single global object called "JSON" is provided with methods
314             C and C compatible with the well-known json2.js
315             library (L), and a method
316             C that provides a Javascript JsonT
317             implementation.
318              
319             A function C is provided which prints to Perl's
320             STDOUT stream.
321              
322             =head1 SUBCLASSING
323              
324             Two subclasses are provided: L and L,
325             but if you need to hook L up to another Javascript engine, it is
326             relatively simple. Just create a Perl class which is a subclass of L.
327             This subclass must implement two required methods and should implement
328             one optional method.
329              
330             =over
331              
332             =item C<< init >>
333              
334             Will be passed a newly created object (let's call it C<< $self >>). It is
335             expected to initialise a Javascript execution context for C<< $self >>, and
336             define two Javascript functions: C (which acts as a shim
337             to C<< $self->_accept_return_value() >>) and C (which acts
338             as a shim to C). It must then call C<< SUPER::init >>.
339              
340             =item C<< engine_eval >>
341              
342             Will be passed an object (C<< $self >>) and a Javascript string. Must evaluate
343             the string in the object's Javascript execution context.
344              
345             =item C<< parameters >>
346              
347             This one is optional to implement it. If you don't implement it, then users
348             will get a warning message if they try to call C<< parameters >> on your
349             subclass.
350              
351             Will be passed an object (C<< $self >>) and a hash of parameters, using the
352             following format:
353              
354             (
355             name1 => 'value1',
356             name2 => [ type2 => 'value2' ],
357             name3 => [ type3 => 'value3', hint => 'hint value' ],
358             )
359              
360             This should have the effect of setting:
361              
362             var name1 = 'value1';
363             var name2 = 'value2';
364             var name3 = 'value3';
365              
366             in the object's Javascript execution context. Parameter types and additional
367             hints may be used to set the correct types in Javascript.
368              
369             =back
370              
371             You are unlikely to need to do anything else when subclassing.
372              
373             If you wish C<< JSON::T->new >> to know about your subclass, then push
374             its name onto C<< @JSON::T::Implementations >>.
375              
376             =head1 BUGS
377              
378             Please report any bugs to L.
379              
380             =head1 SEE ALSO
381              
382             Specification: L.
383              
384             Related modules: L, L, L,
385             L, L.
386              
387             JOM version: L, L.
388              
389             =head1 AUTHOR
390              
391             Toby Inkster Etobyink@cpan.orgE.
392              
393             This module is embeds Stefan Goessner's Javascript implementation of
394             JsonT (version 0.9) to do the heavy lifting.
395              
396             =head1 COPYRIGHT AND LICENCE
397              
398             Copyright 2006 Stefan Goessner.
399              
400             Copyright 2008-2011, 2013-2014 Toby Inkster.
401              
402             Licensed under the Lesser GPL:
403             L.
404              
405             =head1 DISCLAIMER OF WARRANTIES
406              
407             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
408             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
409             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
410              
411             =cut
412              
413             # Here's the Javascript...
414              
415             __DATA__