File Coverage

blib/lib/JSON/YY.pm
Criterion Covered Total %
statement 73 77 94.8
branch 13 16 81.2
condition 7 16 43.7
subroutine 21 23 91.3
pod 8 8 100.0
total 122 140 87.1


line stmt bran cond sub pod time code
1             package JSON::YY;
2              
3 18     18   1518555 use strict;
  18         30  
  18         585  
4 18     18   60 use warnings;
  18         29  
  18         816  
5 18     18   94 use Carp;
  18         22  
  18         6556  
6              
7             our $VERSION = '0.05';
8              
9             require XSLoader;
10             XSLoader::load('JSON::YY', $VERSION);
11              
12             our @EXPORT_OK = qw(encode_json decode_json decode_json_ro);
13              
14             my @DOC_KEYWORDS = qw(jdoc jget jgetp jset jdel jhas jclone jencode
15             jstr jnum jbool jnull jarr jobj jtype jlen jkeys jdecode
16             jiter jnext jkey jpatch jmerge jfrom jvals jeq
17             jpp jraw jread jwrite jpaths jfind
18             jis_obj jis_arr jis_str jis_num jis_int jis_real jis_bool jis_null);
19              
20             # functional API — fast XS path, always utf8
21             *encode_json = \&_xs_encode_json;
22             *decode_json = \&_xs_decode_json;
23             *decode_json_ro = \&_xs_decode_json_ro;
24              
25             my %SETTERS = (
26             utf8 => \&_set_utf8,
27             pretty => \&_set_pretty,
28             canonical => \&_set_canonical,
29             allow_nonref => \&_set_allow_nonref,
30             allow_unknown => \&_set_allow_unknown,
31             allow_blessed => \&_set_allow_blessed,
32             convert_blessed => \&_set_convert_blessed,
33             max_depth => \&_set_max_depth,
34             );
35              
36             sub import {
37 25     25   447 my $class = shift;
38 25         40 my @exports;
39             my @flags;
40 25         33 my $want_doc = 0;
41 25         57 for my $arg (@_) {
42 37 100       125 if ($arg eq ':doc') {
    100          
43 11         19 $want_doc = 1;
44             } elsif ($arg =~ /^-(.+)/) {
45 2         6 push @flags, $1;
46             } else {
47 24         42 push @exports, $arg;
48             }
49             }
50 25         47 my $caller = caller;
51              
52             # activate keywords via XS::Parse::Keyword hint keys
53 25 100       61 if ($want_doc) {
54 11         679 $^H{"JSON::YY/$_"} = 1 for @DOC_KEYWORDS;
55             }
56 25 100       104 if (@flags) {
57 1         6 my $coder = $class->new;
58 1         1 for my $f (@flags) {
59 2 50       5 my $setter = $SETTERS{$f}
60             or Carp::croak("unknown flag: -$f");
61 2         5 $coder->$setter(1);
62             }
63 18     18   99 no strict 'refs';
  18         54  
  18         1905  
64 1     1   9 *{"${caller}::encode_json"} = sub { $coder->encode($_[0]) };
  1         5  
  1         147263  
65 1     1   3 *{"${caller}::decode_json"} = sub { $coder->decode($_[0]) };
  1         3  
  1         918  
66             }
67 25 100       83272 if (@exports) {
68 18     18   84 no strict 'refs';
  18         27  
  18         5517  
69 13         28 for my $e (@exports) {
70             Carp::croak("'$e' is not exported by JSON::YY")
71 24 50       33 unless grep { $_ eq $e } @EXPORT_OK;
  72         129  
72 24         24 *{"${caller}::$e"} = \&{$e};
  24         71  
  24         42  
73             # also enable keyword for exported functions
74 24         84928 $^H{"JSON::YY/$e"} = 1;
75             }
76             }
77             }
78              
79             # chaining setters
80 10   50 10 1 59 sub utf8 { $_[0]->_set_utf8($_[1] // 1); $_[0] }
  10         26  
81 3   50 3 1 23 sub pretty { $_[0]->_set_pretty($_[1] // 1); $_[0] }
  3         81  
82 0   0 0 1 0 sub canonical { $_[0]->_set_canonical($_[1] // 1); $_[0] }
  0         0  
83 2   100 2 1 9 sub allow_nonref { $_[0]->_set_allow_nonref($_[1] // 1); $_[0] }
  2         3  
84 0   0 0 1 0 sub allow_unknown { $_[0]->_set_allow_unknown($_[1] // 1); $_[0] }
  0         0  
85 1   50 1 1 5 sub allow_blessed { $_[0]->_set_allow_blessed($_[1] // 1); $_[0] }
  1         2  
86 2   50 2 1 16 sub convert_blessed { $_[0]->_set_convert_blessed($_[1] // 1); $_[0] }
  2         4  
87 2   50 2 1 11 sub max_depth { $_[0]->_set_max_depth($_[1] // 512); $_[0] }
  2         2  
88              
89             # wrap XS new to accept keyword args
90             {
91             my $orig_new = JSON::YY->can('new');
92 18     18   105 no warnings 'redefine';
  18         32  
  18         3904  
93             *new = sub {
94 17     17   875710 my ($class, %args) = @_;
95 17         104 my $self = $orig_new->($class);
96 17         49 for my $k (keys %args) {
97 8 50       29 my $setter = $SETTERS{$k}
98             or Carp::croak("unknown option: $k");
99 8         21 $self->$setter($args{$k});
100             }
101 17         60 $self;
102             };
103             }
104              
105             # Doc overloading: stringify to JSON, boolean always true, eq/ne deep compare
106             package JSON::YY::Doc;
107             use overload
108 2     2   176894 '""' => sub { JSON::YY::_doc_stringify($_[0]) },
109 1     1   81 'bool' => sub { 1 },
110 1     1   9 'eq' => sub { JSON::YY::_doc_eq($_[0], $_[1]) },
111 1     1   6 'ne' => sub { !JSON::YY::_doc_eq($_[0], $_[1]) },
112 18     18   8331 fallback => 1;
  18         21396  
  18         162  
113              
114             package JSON::YY;
115             1;
116              
117             __END__