File Coverage

blib/lib/JSON/T.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


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