File Coverage

blib/lib/Venus/Json.pm
Criterion Covered Total %
statement 82 101 81.1
branch 18 24 75.0
condition 2 6 33.3
subroutine 18 24 75.0
pod 4 13 30.7
total 124 168 73.8


line stmt bran cond sub pod time code
1             package Venus::Json;
2              
3 2     2   1117 use 5.018;
  2         9  
4              
5 2     2   10 use strict;
  2         4  
  2         40  
6 2     2   9 use warnings;
  2         4  
  2         72  
7              
8             use overload (
9 2         12 '""' => 'explain',
10             '~~' => 'explain',
11             fallback => 1,
12 2     2   11 );
  2         4  
13              
14 2     2   200 use Venus::Class 'attr', 'base', 'with';
  2         4  
  2         14  
15              
16             base 'Venus::Kind::Utility';
17              
18             with 'Venus::Role::Valuable';
19             with 'Venus::Role::Buildable';
20             with 'Venus::Role::Accessible';
21             with 'Venus::Role::Explainable';
22              
23             # ATTRIBUTES
24              
25             attr 'decoder';
26             attr 'encoder';
27              
28             # BUILDERS
29              
30             sub build_arg {
31 4     4 0 8 my ($self, $data) = @_;
32              
33             return {
34 4         14 value => $data
35             };
36             }
37              
38             sub build_args {
39 8     8 0 20 my ($self, $data) = @_;
40              
41 8 50 33     40 if (keys %$data == 1 && exists $data->{value}) {
42 8         25 return $data;
43             }
44             return {
45 0         0 value => $data
46             };
47             }
48              
49             sub build_nil {
50 0     0 0 0 my ($self, $data) = @_;
51              
52             return {
53 0         0 value => $data
54             };
55             }
56              
57             sub build_self {
58 8     8 0 20 my ($self, $data) = @_;
59              
60 8         22 return $self->config;
61             }
62              
63             # METHODS
64              
65             sub assertion {
66 0     0 1 0 my ($self) = @_;
67              
68 0         0 my $assert = $self->SUPER::assertion;
69              
70 0         0 $assert->clear->expression('hashref');
71              
72 0         0 return $assert;
73             }
74              
75             sub config {
76 8     8 0 21 my ($self, $package) = @_;
77              
78 8 50 33     32 $package ||= $self->package
79             or $self->error({throw => 'error_on_config'});
80              
81 8         47 $package = $package->new
82             ->canonical
83             ->allow_nonref
84             ->allow_unknown
85             ->allow_blessed
86             ->convert_blessed
87             ->pretty;
88              
89 8 50       1558 if ($package->can('escape_slash')) {
90 8         121 $package->escape_slash;
91             }
92              
93             # Cpanel::JSON::XS
94 8 50       120 if ($package->isa('Cpanel::JSON::XS')) {
95             $self->decoder(sub {
96 0     0   0 my ($text) = @_;
97 0         0 $package->decode($text);
98 0         0 });
99             $self->encoder(sub {
100 0     0   0 my ($data) = @_;
101 0         0 $package->encode($data);
102 0         0 });
103             }
104              
105             # JSON::XS
106 8 50       40 if ($package->isa('JSON::XS')) {
107             $self->decoder(sub {
108 0     0   0 my ($text) = @_;
109 0         0 $package->decode($text);
110 0         0 });
111             $self->encoder(sub {
112 0     0   0 my ($data) = @_;
113 0         0 $package->encode($data);
114 0         0 });
115             }
116              
117             # JSON::PP
118 8 50       49 if ($package->isa('JSON::PP')) {
119             $self->decoder(sub {
120 1     1   2 my ($text) = @_;
121 1         53 $package->decode($text);
122 8         68 });
123             $self->encoder(sub {
124 7     7   17 my ($data) = @_;
125 7         23 $package->encode($data);
126 8         56 });
127             }
128              
129 8         20 return $self;
130             }
131              
132             sub decode {
133 1     1 1 3 my ($self, $data) = @_;
134              
135             # double-traversing the data structure due to lack of serialization hooks
136 1         3 return $self->set(FROM_BOOL($self->decoder->($data)));
137             }
138              
139             sub encode {
140 7     7 1 58 my ($self) = @_;
141              
142             # double-traversing the data structure due to lack of serialization hooks
143 7         22 return $self->encoder->(TO_BOOL($self->get));
144             }
145              
146             sub explain {
147 2     2 0 139 my ($self) = @_;
148              
149 2         6 return $self->encode;
150             }
151              
152             sub package {
153 13     13 0 54 my ($self) = @_;
154              
155 13         16 state $engine;
156              
157 13 100       52 return $engine if defined $engine;
158              
159 2         9 my %packages = (
160             'JSON::XS' => '3.0',
161             'JSON::PP' => '2.27105',
162             'Cpanel::JSON::XS' => '4.09',
163             );
164 2         11 for my $package (
165             grep defined,
166             $ENV{VENUS_JSON_PACKAGE},
167             qw(Cpanel::JSON::XS JSON::XS JSON::PP)
168             )
169             {
170 6         22 my $criteria = "require $package; $package->VERSION($packages{$package})";
171 6 100       6 if (do {local $@; eval "$criteria"; $@}) {
  6         9  
  6         393  
  6         43  
172 4         12 next;
173             }
174             else {
175 2         7 $engine = $package;
176 2         4 last;
177             }
178             }
179              
180 2         9 return $engine;
181             }
182              
183             sub FROM_BOOL {
184 5     5 0 580 my ($value) = @_;
185              
186 5         22 require Venus::Boolean;
187              
188 5 100       17 if (ref($value) eq 'HASH') {
189 1         5 for my $key (keys %$value) {
190 2         8 $value->{$key} = FROM_BOOL($value->{$key});
191             }
192 1         6 return $value;
193             }
194              
195 4 100       13 if (ref($value) eq 'ARRAY') {
196 1         3 for my $key (keys @$value) {
197 2         9 $value->[$key] = FROM_BOOL($value->[$key]);
198             }
199 1         4 return $value;
200             }
201              
202 3         31 return Venus::Boolean::TO_BOOL(Venus::Boolean::FROM_BOOL($value));
203             }
204              
205             sub TO_BOOL {
206 24     24 0 34 my ($value) = @_;
207              
208 24         1316 require Venus::Boolean;
209              
210 24 100       62 if (ref($value) eq 'HASH') {
211 3         15 $value = {
212             %$value
213             };
214 3         19 for my $key (keys %$value) {
215 9         27 $value->{$key} = TO_BOOL($value->{$key});
216             }
217 3         17 return $value;
218             }
219              
220 21 100       39 if (ref($value) eq 'ARRAY') {
221 5         19 $value = [
222             @$value
223             ];
224 5         21 for my $key (keys @$value) {
225 8         34 $value->[$key] = TO_BOOL($value->[$key]);
226             }
227 5         18 return $value;
228             }
229              
230 16         36 return Venus::Boolean::TO_BOOL_JPO($value);
231             }
232              
233             # ERRORS
234              
235             sub error_on_config {
236 1     1 1 3 my ($self) = @_;
237              
238             return {
239 1         4 name => 'on.config',
240             message => 'No suitable JSON package',
241             raise => true,
242             };
243             }
244              
245             1;
246              
247              
248              
249             =head1 NAME
250              
251             Venus::Json - Json Class
252              
253             =cut
254              
255             =head1 ABSTRACT
256              
257             Json Class for Perl 5
258              
259             =cut
260              
261             =head1 SYNOPSIS
262              
263             package main;
264              
265             use Venus::Json;
266              
267             my $json = Venus::Json->new(
268             value => { name => ['Ready', 'Robot'], version => 0.12, stable => !!1, }
269             );
270              
271             # $json->encode;
272              
273             =cut
274              
275             =head1 DESCRIPTION
276              
277             This package provides methods for reading and writing L
278             data. B This package requires that a suitable JSON library is installed,
279             currently either C C<3.0+>, C C<2.27105+>, or
280             C C<4.09+>. You can use the C environment
281             variable to include or prioritize your preferred JSON library.
282              
283             =cut
284              
285             =head1 ATTRIBUTES
286              
287             This package has the following attributes:
288              
289             =cut
290              
291             =head2 decoder
292              
293             decoder(CodeRef)
294              
295             This attribute is read-write, accepts C<(CodeRef)> values, and is optional.
296              
297             =cut
298              
299             =head2 encoder
300              
301             encoder(CodeRef)
302              
303             This attribute is read-write, accepts C<(CodeRef)> values, and is optional.
304              
305             =cut
306              
307             =head1 INHERITS
308              
309             This package inherits behaviors from:
310              
311             L
312              
313             =cut
314              
315             =head1 INTEGRATES
316              
317             This package integrates behaviors from:
318              
319             L
320              
321             L
322              
323             L
324              
325             L
326              
327             =cut
328              
329             =head1 METHODS
330              
331             This package provides the following methods:
332              
333             =cut
334              
335             =head2 decode
336              
337             decode(Str $json) (Any)
338              
339             The decode method decodes the JSON string, sets the object value, and returns
340             the decoded value.
341              
342             I>
343              
344             =over 4
345              
346             =item decode example 1
347              
348             # given: synopsis;
349              
350             my $decode = $json->decode('{"codename":["Ready","Robot"],"stable":true}');
351              
352             # { codename => ["Ready", "Robot"], stable => 1 }
353              
354             =back
355              
356             =cut
357              
358             =head2 encode
359              
360             encode() (Str)
361              
362             The encode method encodes the objects value as a JSON string and returns the
363             encoded string.
364              
365             I>
366              
367             =over 4
368              
369             =item encode example 1
370              
371             # given: synopsis;
372              
373             my $encode = $json->encode;
374              
375             # '{ "name": ["Ready", "Robot"], "stable": true, "version": 0.12 }'
376              
377             =back
378              
379             =cut
380              
381             =head1 ERRORS
382              
383             This package may raise the following errors:
384              
385             =cut
386              
387             =over 4
388              
389             =item error: C
390              
391             This package may raise an error_on_config exception.
392              
393             B
394              
395             # given: synopsis;
396              
397             my $input = {
398             throw => 'error_on_config',
399             };
400              
401             my $error = $json->catch('error', $input);
402              
403             # my $name = $error->name;
404              
405             # "on_config"
406              
407             # my $message = $error->message;
408              
409             # "No suitable JSON package"
410              
411             =back
412              
413             =head1 AUTHORS
414              
415             Awncorp, C
416              
417             =cut
418              
419             =head1 LICENSE
420              
421             Copyright (C) 2000, Al Newkirk.
422              
423             This program is free software, you can redistribute it and/or modify it under
424             the terms of the Apache license version 2.0.
425              
426             =cut