File Coverage

blib/lib/Jasonify.pm
Criterion Covered Total %
statement 136 159 85.5
branch 98 152 64.4
condition 6 9 66.6
subroutine 46 58 79.3
pod 10 13 76.9
total 296 391 75.7


line stmt bran cond sub pod time code
1 2     2   14079 use v5.14;
  2         7  
2 2     2   11 use warnings;
  2         4  
  2         118  
3              
4             package Jasonify v0.20.052;
5             # ABSTRACT: Just Another Serialized Object Notation library.
6              
7              
8 2     2   14 use Carp (); #qw( carp );
  2         4  
  2         59  
9 2     2   1291 use Datify v0.20.052 ();
  2         37256  
  2         56  
10 2     2   13 use Scalar::Util (); #qw( blessed looks_like_number reftype );
  2         4  
  2         41  
11 2     2   9 use String::Tools qw( subst ); #qw( );
  2         3  
  2         118  
12              
13 2     2   887 use parent 'Datify';
  2         563  
  2         12  
14              
15              
16              
17              
18             ### Accessor ###
19              
20              
21              
22              
23             ### Setter ###
24              
25              
26              
27              
28             __PACKAGE__->set(
29             # Varify/Encode options
30             #name => '',
31             #assign => undef,
32             #list => undef,
33             list_sep => ', ',
34             beautify => undef,
35             );
36              
37              
38              
39             __PACKAGE__->set(
40             # Undefify options
41             null => 'null',
42             );
43              
44              
45              
46             __PACKAGE__->set(
47             # Booleanify options
48             false => 'false',
49             true => 'true',
50             );
51              
52              
53              
54             __PACKAGE__->set(
55             # Stringify options
56             quote => '"',
57             #quote1 => undef,
58             quote2 => '"',
59             #q1 => undef,
60             #q2 => undef,
61             #sigils => undef,
62             longstr => -1,
63             #encode1 => undef,
64             encode2 => {
65             map( { $_ => sprintf( '\\u%04x', $_ ) }
66             0x00 .. 0x1f, 0x7f, # Control characters (C0)
67             0x80 .. 0x9f, # Control characters (C1)
68             0x2028, 0x2029, # Characters not allowed by Javascript
69             ),
70             # Special cases
71             map( { ord( eval qq!"$_"! ) => $_ } qw( \b \t \n \r \" \\\\ ) ),
72             utf => 16,
73             byte => '\\u00%02x',
74             wide => '\\u%04x',
75             },
76             #qpairs => undef,
77             #qquotes => undef,
78             );
79              
80              
81              
82             __PACKAGE__->set(
83             # Numify options
84             infinite => '"Infinity"',
85             -infinite => '"-Infinity"',
86             nonnumber => '"NaN"',
87             #num_sep => undef,
88             );
89              
90              
91              
92             __PACKAGE__->set(
93             # Lvalueify options
94             lvalue => '$lvalue',
95             );
96              
97              
98              
99             __PACKAGE__->set(
100             # Vstringify options
101             vformat => '"\\u%0*v4x"',
102             vsep => '\\u',
103             );
104              
105              
106             #=option Regexpify options
107             #
108             #=over
109             #
110             #=item ...
111             #
112             #=back
113             #
114             #=cut
115             #
116             #__PACKAGE__->set(
117             # # Regexpify options
118             # #quote3 => undef,
119             # #q3 => undef,
120             # #encode3 => undef,
121             #);
122              
123              
124              
125             __PACKAGE__->set(
126             # Arrayify options
127             array_ref => '[$_]',
128             );
129              
130              
131              
132             __PACKAGE__->set(
133             # Hashify options
134             hash_ref => '{$_}',
135             pair => '$key : $value',
136             keysort => \&Datify::keysort,
137             keyfilter => undef,
138             keyfilterdefault => 1,
139             #keywords => undef,
140             );
141              
142              
143              
144             __PACKAGE__->set(
145             # Objectify options
146             json_method => 'TO_JSON',
147             object => '$data',
148             #object => '{$class_str : $data}',
149             overloads => [qw( "" 0+ )],
150             tag => undef,
151             #tag => '($class_str)$data',
152             tag_method => 'FREEZE',
153             );
154              
155              
156              
157             __PACKAGE__->set(
158             # Ioify options
159             io => 'null',
160             );
161              
162              
163              
164             __PACKAGE__->set(
165             # Codeify options
166             code => 'null',
167             #codename => undef,
168             #body => undef,
169             );
170              
171              
172              
173             __PACKAGE__->set(
174             # Refify options
175             reference => '$_',
176             dereference => '$referent$place',
177             #nested => undef,
178             );
179              
180              
181              
182             __PACKAGE__->set(
183             # Formatify options
184             format => 'null',
185             );
186              
187              
188              
189             # Override Datify::booleanify() for SCALAR refs
190             sub booleanify {
191 72     72 1 1502 my $self = &Datify::self;
192 72 50       487 local $_ = shift if @_;
193 72 100       139 return $self->undefify unless defined;
194 63 100       126 return $self->booleanify($$_) if 'SCALAR' eq ref;
195 59 100       178 return $_ ? $Jasonify::Boolean::true : $Jasonify::Boolean::false;
196             }
197              
198              
199              
200             # Override Datify::keyify() to appropriately stringify all keys
201             sub keyify {
202 138     138 1 23111 my $self = &Datify::self;
203 138 50       937 local $_ = shift if @_;
204              
205 138 50       736 return Scalar::Util::looks_like_number($_)
    100          
    100          
    100          
206             ? (
207             defined( $_ <=> 0 )
208             ? (
209             $_ == "Infinity" ? $Jasonify::Number::inf
210             : $_ == "-Infinity" ? $Jasonify::Number::ninf
211             : $self->stringify($_)
212             )
213             : $Jasonify::Number::nan
214             )
215             : $self->stringify($_);
216             }
217              
218             sub _objectify_via {
219 4     4   21 my $self = shift;
220 4         8 my $object = shift;
221              
222 4 100       10 if ( my $method_name = shift ) {
223 2         46 return $object->can($method_name);
224             }
225 2         9 return;
226             }
227             sub _objectify_via_tag {
228 2     2   84 my $self = shift;
229 2         6 my $object = shift;
230              
231 2   33     5 my $tag_method = $self->get('tag') && $self->get('tag_method');
232 2         26 return $self->_objectify_via( $object => $tag_method );
233             }
234             sub _objectify_via_json {
235 2     2   3 my $self = shift;
236 2         3 my $object = shift;
237              
238 2         5 return $self->_objectify_via( $object => $self->get('json_method') );
239             }
240              
241              
242              
243             # Override Datify::objectify() to appropriately stringify objects
244             sub objectify {
245 55     55 1 91 my $self = &Datify::self;
246 55         287 my $object = shift;
247              
248 55 50       128 return $self->scalarify($object)
249             unless defined( my $class = Scalar::Util::blessed($object) );
250              
251 55         123 my $object_str = $self->get('object');
252              
253 55         530 my $data;
254 55 100       123 if (0) {
    50          
    50          
    50          
    50          
255 0         0 } elsif ( my $code = $self->_find_handler($class) ) {
256 53         1079 return $self->$code($object);
257             } elsif ( my $tag = $self->_objectify_via_tag($object) ) {
258 0         0 $object_str = $self->get('tag');
259 0         0 $data = $self->arrayify( $object->$tag('JSON') );
260             } elsif ( my $to_json = $self->_objectify_via_json($object) ) {
261 0         0 $data = $self->scalarify( $object->$to_json() );
262             } elsif ( my $method = $self->overloaded($object) ) {
263 0         0 $data = $self->scalarify( $object->$method() );
264             } elsif ( my $attrkeyvals = $object->can('_attrkeyvals') ) {
265             # TODO: Look this up via meta-objects and such.
266 0         0 $data = $self->hashify( $object->$attrkeyvals() );
267             } else {
268 2         172 $data = Scalar::Util::reftype $object;
269              
270 2 0       18 $data
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
271             = $data eq 'ARRAY' ? $self->arrayify( @$object )
272             : $data eq 'CODE' ? $self->codeify( $object )
273             : $data eq 'FORMAT' ? $self->formatify( $object )
274             : $data eq 'GLOB' ? $self->globify( $object )
275             : $data eq 'HASH' ? $self->hashify( $object )
276             : $data eq 'IO' ? $self->ioify( $object )
277             : $data eq 'REF' ? $self->scalarify( $$object )
278             : $data eq 'REGEXP' ? $self->regexpify( $object )
279             : $data eq 'SCALAR' ? $self->scalarify( $$object )
280             : $self->undefify;
281             }
282              
283 2         204 return subst(
284             $object_str,
285             class_str => $self->stringify($class),
286             class => $class,
287             data => $data,
288             );
289             }
290              
291              
292              
293             # Override Datify::regexpify() to appropriately stringify regular expressions
294             sub regexpify {
295 2     2 1 5 my $self = &Datify::self;
296 2 50       13 local $_ = shift if @_;
297              
298 2         7 return $self->stringify($_);
299             }
300              
301             # Override Datify::varify so that it throws an error
302             sub varify;
303              
304              
305              
306             # Override Datify::vstringify so that it encodes a vstring as appropriate
307             sub vstringify {
308 2     2 1 6 my $self = &Datify::self;
309 2 50       15 local $_ = shift if @_;
310              
311             # Encode as a vstring if vformat has been specified
312             # or as a regular string if vformat has not been specified
313 2 50       7 return $self->get('vformat')
314             ? $self->SUPER::vstringify($_)
315             : $self->stringify($_);
316             }
317              
318              
319              
320             # Override Datify::scalarify to properly handle all of the various types
321             sub _scalarify {
322 347     347   86858 my $self = &Datify::self;
323 347 50       2123 local $_ = shift if @_;
324              
325 347 100       678 return $self->undefify unless defined $_;
326              
327 336 100       801 if ( defined( my $blessed = Scalar::Util::blessed($_) ) ) {
328             return
329 57 100       170 $blessed eq 'Regexp' ? $self->regexpify($_)
330             : $self->objectify($_);
331             }
332              
333 279         512 my $ref = Scalar::Util::reftype $_;
334 279 100       523 if ( not $ref ) {
335             # Handle GLOB, LVALUE, and VSTRING
336 164         283 my $ref2 = ref \$_;
337             return
338 164 100 66     1088 $ref2 eq 'GLOB' ? $self->globify($_)
    100          
    50          
    50          
339             : $ref2 eq 'LVALUE' ? $self->lvalueify($_)
340             : $ref2 eq 'VSTRING' ? $self->vstringify($_)
341             : $ref2 eq 'SCALAR' && Scalar::Util::looks_like_number($_)
342             ? $self->numify($_)
343             : $self->stringify($_)
344             ;
345             }
346              
347             return
348 115 50       633 $ref eq 'ARRAY' ? $self->arrayify(@$_)
    100          
    50          
    100          
    100          
    50          
    100          
    50          
    50          
    100          
    100          
349             : $ref eq 'CODE' ? $self->codeify($_)
350             : $ref eq 'FORMAT' ? $self->formatify($_)
351             : $ref eq 'GLOB' ? $self->globify($$_)
352             : $ref eq 'HASH' ? $self->hashify($_)
353             : $ref eq 'IO' ? $self->ioify($_)
354             : $ref eq 'LVALUE' ? $self->booleanify($$_)
355             : $ref eq 'REF' ? $self->refify($$_)
356             : $ref eq 'REGEXP' ? $self->regexpify($_) # ???
357             : $ref eq 'SCALAR' ? $self->booleanify($$_)
358             : $ref eq 'VSTRING' ? $self->booleanify($$_)
359             : $self->objectify($_) # ???
360             ;
361             }
362              
363              
364              
365             # TODO
366             sub decode;
367              
368              
369              
370             sub encode {
371 53     53 1 3459 my $self = &Datify::self;
372 53 50       747 return unless @_;
373              
374 53         106 my @return = map { $self->scalarify($_) } @_;
  53         143  
375              
376 51         2564 $self->_cache_reset();
377              
378 51 50       763 return @_ == 1 ? $return[0] : @return;
379             }
380              
381              
382              
383             sub boolean {
384 11     11 1 5637 &Datify::class;
385 11 100       192 return @_ ? Jasonify::Boolean::bool( $_[-1] ) : 'Jasonify::Boolean';
386             }
387             *bool = \&boolean;
388              
389              
390              
391             sub literal {
392 10     10 1 2911 &Datify::class;
393 10 100       174 return @_ ? Jasonify::Literal->new( $_[-1] ) : 'Jasonify::Literal';
394             }
395              
396              
397              
398             sub number {
399 13     13 1 3053 &Datify::class;
400 13         183 my $count = scalar @_;
401             return
402 13 100       48 $count >= 2 ? Jasonify::Number->formatted(@_)
    100          
403             : $count == 1 ? Jasonify::Number->number(shift)
404             : 'Jasonify::Number'
405             ;
406             }
407              
408              
409              
410             sub string {
411 5     5 1 1176 &Datify::class;
412 5 100       89 return @_ ? Jasonify::Literal->string( $_[-1] ) : 'Jasonify::Literal';
413             }
414              
415             ### Private Methods & Settings ###
416             ### Do not use these methods & settings outside of this package,
417             ### they are subject to change or disappear at any time.
418 3003     3003   197823 sub _settings() { \state %SETTINGS }
419              
420             __PACKAGE__->set(
421             _cache_hit => 1, # Sets the caching to use the final representation
422             # or die if that doesn't exist
423             );
424              
425              
426             package
427             Jasonify::Literal;
428              
429 2     2   3597 use Scalar::Util (); #qw( looks_like_number );
  2         5  
  2         69  
430              
431             use overload
432 2         25 'bool' => 'bool',
433             '""' => 'as_string',
434 2     2   12 ;
  2         4  
435              
436             our $null = bless \do { my $null = Jasonify->get('null') }, __PACKAGE__;
437             our $false = bless \do { my $false = Jasonify->get('false') }, __PACKAGE__;
438             our $true = bless \do { my $true = Jasonify->get('true') }, __PACKAGE__;
439              
440 13     13 0 26 sub Jasonify::jasonify_literalify { $_[1]->as_string }
441             # OR
442             #Jasonify->add_handler( sub { $_[1]->as_string } );
443              
444 0     0   0 sub null() { $null }
445 0     0   0 sub false() { $false }
446 0     0   0 sub true() { $true }
447              
448             sub new {
449 26     26   59 my $class = &Datify::class;
450 26         321 my $literal = shift;
451 26 50       68 return $null unless defined($literal);
452 26 50       54 return $false unless length( $literal);
453 26         100 return bless \$literal, $class;
454             }
455             sub string {
456 4     4   14 @_ = ( shift, Jasonify->stringify(@_) );
457 4         2463 goto &new;
458             }
459             #sub comment {
460             # $_[0]->new(
461             # "# " . join( "\n# ", map { split /\n/ } @_[ 1 .. $#_ ] ) . "\n" );
462             #}
463              
464 180     180   7172 sub as_string { ${ $_[0] } }
  180         683  
465             sub bool {
466 25     25   3787 my $literal = ${ $_[0] };
  25         51  
467             return
468 25   100     307 $literal ne $$null
469             && $literal ne $$false
470             && $literal ne '""'
471             && $literal ne '"0"'
472             && !( Scalar::Util::looks_like_number($literal) && $literal == 0 );
473             }
474              
475             package
476             Jasonify::Number;
477              
478 2     2   978 use Scalar::Util (); #qw( looks_like_number );
  2         4  
  2         69  
479              
480             use overload
481 2         9 '0+' => 'as_num',
482             'neg' => 'negate',
483              
484             '<=>' => 'comparen',
485             'cmp' => 'compares',
486 2     2   17 ;
  2         5  
487 2     2   246 use parent -norequire => 'Jasonify::Literal';
  2         4  
  2         17  
488              
489             our $nan = bless \do { my $nan = Jasonify->get('nonnumber') }, __PACKAGE__;
490             our $inf = bless \do { my $inf = Jasonify->get( 'infinite') }, __PACKAGE__;
491             our $ninf = bless \do { my $ninf = Jasonify->get('-infinite') }, __PACKAGE__;
492              
493 20     20 0 45 sub Jasonify::jasonify_numberify { $_[1]->as_string }
494             # OR
495             #Jasonify->add_handler( sub { $_[1]->as_string } );
496              
497 0     0   0 sub nan() { $nan }
498 0     0   0 sub inf() { $inf }
499 0     0   0 sub ninf() { $ninf }
500              
501             my $number_regex = do {
502             my $digit09 = '[0123456789]';
503             my $digit19 = '[123456789]';
504             my $integer = "(?:0|$digit19+$digit09*)";
505             my $decimal = "(?:\.$digit09+)";
506             qr/-?$integer$decimal?(?:[Ee][+-]?$integer)?/;
507             };
508              
509 0 0   0   0 sub comparen { ( $_[2] ? -1 : +1 ) * ( $_[0]->as_num <=> $_[1] ) }
510 0 0   0   0 sub compares { ( $_[2] ? -1 : +1 ) * ( ${ $_[0] } cmp $_[1] ) }
  0         0  
511 0     0   0 sub as_num { eval ${ $_[0] } }
  0         0  
512             sub negate {
513 0     0   0 my $num = ${ $_[0] };
  0         0  
514             return
515 0 0       0 $num eq $$nan ? $nan
    0          
    0          
516             : $num eq $$inf ? $ninf
517             : $num eq $$ninf ? $inf
518 0 0       0 : $_[0]->number( $num =~ s/\A(-?)/$1 ? '' : '-'/er )
519             ;
520             }
521             sub number {
522 13     13   33 my $class = &Datify::class;
523 13         172 my $num = shift;
524 13 50       49 Carp::croak( "Not a number ", $num )
525             unless ( Scalar::Util::looks_like_number($num) );
526              
527             return
528 13 50       264 not( defined( $num <=> 0 ) ) ? $nan
    50          
    50          
    50          
529             : $num == 'Infinity' ? $inf
530             : $num == '-Infinity' ? $ninf
531             : $num =~ /\A$number_regex\z/ ? $class->new($num)
532             : Carp::croak( "Malformed number ", $num );
533             }
534              
535 9     9   115 sub formatted { return shift()->number( sprintf( shift(), @_ ) ) }
536 0     0   0 sub integer { return shift()->formatted( '%d', shift() ) }
537 0     0   0 sub float { return shift()->formatted( '%f', shift() ) }
538              
539             package
540             Jasonify::Boolean;
541              
542 2     2   1353 use Scalar::Util (); #qw( blessed );
  2         10  
  2         89  
543              
544             use overload
545 2         11 'bool' => 'value',
546             '0+' => 'value',
547             '""' => 'as_string',
548              
549             '<=>' => 'compare',
550             'cmp' => 'compare',
551              
552             '!' => 'negate',
553 2     2   11 ;
  2         4  
554              
555             our $false = bless \do { my $false = 0 }, __PACKAGE__;
556             our $true = bless \do { my $true = 1 }, __PACKAGE__;
557              
558 20     20 0 40 sub Jasonify::jasonify_booleanify { $_[1]->as_string }
559             # OR
560             #Jasonify->add_handler( sub { $_[1]->as_string } );
561              
562 2     2   6 sub false() { $false }
563 2     2   6 sub true() { $true }
564              
565 4     4   5 sub value { ${ $_[0] } }
  4         16  
566             sub as_string {
567 93 100   93   4431 ${ $_[0] } ? $Jasonify::Literal::true : $Jasonify::Literal::false;
  93         307  
568             }
569              
570 44 50   44   5975 sub compare { ( $_[2] ? -1 : +1 ) * ( ${ $_[0] } <=> ${ bool( $_[1] ) } ) }
  44         87  
  44         90  
571              
572 2 100   2   1229 sub negate { bool($_[0]) ? $false : $true }
573              
574             sub bool($) {
575             is_bool( $_[0] )
576             ? $_[0]
577             : ref( $_[0] ) eq 'SCALAR'
578 56 100   56   98 ? ${ $_[0] } ? $true : $false
  21 100       205  
    100          
    100          
579             : $_[0] ? $true : $false
580             ;
581             }
582 56 100   56   586 sub is_bool($) { Scalar::Util::blessed( $_[0] ) && $_[0]->isa(__PACKAGE__) }
583              
584             1;
585              
586             __END__