File Coverage

blib/lib/Data/Dump/JavaScript.pm
Criterion Covered Total %
statement 64 66 96.9
branch 23 30 76.6
condition 4 9 44.4
subroutine 17 17 100.0
pod 3 3 100.0
total 111 125 88.8


line stmt bran cond sub pod time code
1             package Data::Dump::JavaScript;
2             $Data::Dump::JavaScript::VERSION = '0.002';
3 1     1   84992 use strict;
  1         10  
  1         29  
4 1     1   5 use warnings;
  1         2  
  1         25  
5 1     1   5 use Exporter 'import';
  1         1  
  1         27  
6 1     1   4 use Scalar::Util 'blessed';
  1         2  
  1         51  
7 1     1   7 use Encode ();
  1         1  
  1         14  
8 1     1   5 use B;
  1         1  
  1         1134  
9              
10             # ABSTRACT: Pretty printing of data structures as JavaScript
11              
12              
13             our @EXPORT_OK = qw( dump_javascript false true );
14              
15             # Literal names
16             # Users may override Booleans with literal 0 or 1 if desired.
17             our($FALSE, $TRUE) = map { bless \(my $dummy = $_), 'Data::Dump::JavaScript::_Bool' } 0, 1;
18              
19             # Escaped special character map with u2028 and u2029
20             my %ESCAPE = (
21             '"' => '"',
22             '\\' => '\\',
23             '/' => '/',
24             'b' => "\x08",
25             'f' => "\x0c",
26             'n' => "\x0a",
27             'r' => "\x0d",
28             't' => "\x09",
29             'u2028' => "\x{2028}",
30             'u2029' => "\x{2029}"
31             );
32             my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
33              
34             for(0x00 .. 0x1f) {
35             my $packed = pack 'C', $_;
36             $REVERSE{$packed} = sprintf '\u%.4X', $_ unless defined $REVERSE{$packed};
37             }
38              
39             my $indent_level;
40             # standard and semi-standard JS default
41             my $indent_count = 2;
42              
43              
44 2     2 1 586 sub false () {$FALSE} ## no critic (prototypes)
45              
46              
47 3     3 1 1780 sub true () {$TRUE} ## no critic (prototypes)
48              
49              
50             sub dump_javascript {
51 35     35 1 18069 $indent_level = 0;
52 35         95 Encode::encode 'UTF-8', _encode_value(shift);
53             }
54              
55             sub _encode_key {
56 7     7   16 my $str = shift;
57 7         18 $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
58 7         20 return "$str";
59             }
60              
61             sub _encode_array {
62 33     33   51 my $str = '[';
63             $str .= "\n"
64 33 100       40 if scalar @{$_[0]} > 1;
  33         81  
65 33         55 $indent_level++;
66 33 100       48 $str .= join(",\n", map { scalar @{$_[0]} > 1 ? _get_indented(_encode_value($_)) : _encode_value($_) } @{$_[0]});
  33         47  
  33         86  
  33         70  
67 33         63 $indent_level--;
68 33 100       40 $str .= scalar @{$_[0]} > 1
  33         510  
69             ? "\n" . _get_indented("]")
70             : ']';
71             }
72              
73             sub _encode_object {
74 8     8   15 my $object = shift;
75 8         11 my $str = '{';
76 8 100       30 $str .= "\n"
77             if keys %$object > 0;
78 8         10 $indent_level++;
79 8         26 my @pairs = map { _get_indented(_encode_key($_)) . ': ' . _encode_value($object->{$_}) }
  7         15  
80             sort keys %$object;
81             #$str .= join(",\n", @pairs) . "\n";
82 8         21 $str .= join(",\n", @pairs);
83 8         13 $indent_level--;
84 8 100       21 $str .= keys %$object > 0
85             ? "\n" . _get_indented("}")
86             : '}';
87 8         33 return $str;
88             }
89              
90             sub _encode_string {
91 15     15   25 my $str = shift;
92 15         97 $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
93 15         74 return "'$str'";
94             }
95              
96             sub _encode_value {
97 75     75   135 my $value = shift;
98              
99             # Reference
100 75 100       179 if (my $ref = ref $value) {
101              
102             # Object
103 46 100       105 return _encode_object($value) if $ref eq 'HASH';
104              
105             # Array
106 38 100       101 return _encode_array($value) if $ref eq 'ARRAY';
107              
108             # True or false
109 5 0       12 return $$value ? 'true' : 'false' if $ref eq 'SCALAR';
    50          
110 5 100       41 return $value ? 'true' : 'false' if $ref eq 'Data::Dump::JavaScript::_Bool';
    50          
111              
112             # Blessed reference with TO_JSON method
113 0 0 0     0 if (blessed $value && (my $sub = $value->can('TO_JSON'))) {
114 0         0 return _encode_value($value->$sub);
115             }
116             }
117              
118             # Null
119 29 100       71 return 'null' unless defined $value;
120              
121              
122             # Number (bitwise operators change behavior based on the internal value type)
123              
124 26 50 66     272 return $value
      66        
125             if B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK)
126             # filter out "upgraded" strings whose numeric form doesn't strictly match
127             && 0 + $value eq $value
128             # filter out inf and nan
129             && $value * 0 == 0;
130              
131             # String
132 15         38 return _encode_string($value);
133             }
134              
135             sub _get_indented {
136 35     35   135 return ' ' x ( $indent_level * $indent_count ) . shift;
137             }
138              
139             # Emulate boolean type
140             package Data::Dump::JavaScript::_Bool;
141             $Data::Dump::JavaScript::_Bool::VERSION = '0.002';
142 1     1   1301 use overload '""' => sub { ${$_[0]} }, fallback => 1;
  1     10   1034  
  1         8  
  10         16  
  10         31  
143              
144              
145             1;
146              
147             __END__