File Coverage

blib/lib/JSON/Lines.pm
Criterion Covered Total %
statement 86 97 88.6
branch 27 46 58.7
condition 5 6 83.3
subroutine 15 16 93.7
pod 8 8 100.0
total 141 173 81.5


line stmt bran cond sub pod time code
1             package JSON::Lines;
2 8     8   574444 use 5.006; use strict; use warnings; our $VERSION = '0.02';
  8     8   95  
  8     8   42  
  8         15  
  8         167  
  8         39  
  8         13  
  8         408  
3 8     8   5686 use JSON; use base 'Import::Export';
  8     8   103951  
  8         45  
  8         1151  
  8         17  
  8         3730  
4              
5             our ($JSON, $LINES, %EX);
6             BEGIN {
7 8     8   140913 $JSON = JSON->new;
8 8         55 $LINES = qr{ ([\[\{] (?: (?> [^\[\]\{\}]+ ) | (??{ $LINES }) )* [\]\}]) }x;
9 8         9290 %EX = (
10             jsonl => [qw/all/]
11             );
12             }
13              
14             sub jsonl {
15 4 50   4 1 3606 my %args = (scalar @_ == 1 ? %{$_[0]} : @_);
  0         0  
16 4         21 my $self = __PACKAGE__->new(%args);
17             return $args{file}
18             ? $self->encode_file($args{file}, $args{data})
19             : $self->encode($args{data})
20 4 50       18 if ($args{encode});
    100          
21             return $args{file}
22             ? $self->decode_file($args{file})
23             : $self->decode($args{data})
24 2 50       11 if ($args{decode});
    50          
25             }
26              
27             sub new {
28 10 50   10 1 603 my ($pkg, %args) = (shift, scalar @_ == 1 ? %{$_[0]} : @_);
  0         0  
29 10         41 my $self = bless { headers => [] }, $pkg;
30 10   66     172 exists $args{$_} && $JSON->$_($args{$_}) for qw/json pretty canonical/;
31 10         96 $self->{$_} = $args{$_} for qw/parse_headers error_cb success_cb/;
32 10         36 $self;
33             }
34              
35             sub encode {
36 11 100   11 1 2731 my ($self, @data) = (shift, scalar @_ == 1 ? @{$_[0]} : @_);
  7         17  
37             @data = $self->_parse_headers(@data)
38 11 100       43 if ($self->{parse_headers});
39 11         17 my $stream;
40 11         30 for (@data) {
41 28         46 my $json = eval { $JSON->encode($_) };
  28         212  
42 28 50       75 if ($@) {
43 0 0       0 if ($self->{error_cb}) {
44 0         0 $self->{error_cb}->($@, $_);
45             } else {
46 0         0 die $@;
47             }
48             } else {
49 28 50       77 $self->{success_cb}->($json) if $self->{success_cb};
50 28 100 100     186 $stream .= ($stream && $stream !~ m/\n$/ ? "\n" : "") . $json;
51             }
52             }
53 11         81 return $self->{stream} = $stream;
54             }
55              
56             sub encode_file {
57 1     1 1 23 my ($self, $file) = (shift, shift);
58 1         105 open my $fh, '>', $file;
59 1         11 print $fh $self->encode(@_);
60 1         62 close $fh;
61 1         9 return $file;
62             }
63              
64             sub decode {
65 9     9 1 4605 my ($self, $string) = @_;
66 9         48 my @lines;
67 9         111 for ($string =~ m/$LINES/g) {
68 26         39 my $struct = eval { $JSON->decode($_) };
  26         167  
69 26 50       61 if ($@) {
70 0 0       0 if ($self->{error_cb}) {
71 0         0 $self->{error_cb}->($@, $_);
72             } else {
73 0         0 die $@;
74             }
75             } else {
76 26 50       64 $self->{success_cb}->($struct) if $self->{success_cb};
77 26         57 push @lines, $struct;
78             }
79             }
80             @lines = $self->_deparse_headers(@lines)
81 9 100       34 if ($self->{parse_headers});
82 9 100       59 return wantarray ? @lines : \@lines;
83             }
84              
85             sub decode_file {
86 1     1 1 830 my ($self, $file) = (shift, shift);
87 1         38 open my $fh, '<', $file;
88 1         4 my $content = do { local $/; <$fh> };
  1         5  
  1         35  
89 1         12 close $fh;
90 1         5 return $self->decode($content);
91             }
92              
93             sub add_line {
94 2     2 1 1451 my $stream = $_[0]->{stream};
95 2         16 my $add = $_[0]->encode([$_[1]]);
96 2 50       11 $_[0]->{stream} = ($stream ? "$stream\n" : "") . $add;
97 2         7 $_[0]->{stream};
98             }
99              
100             sub clear_stream {
101 0     0 1 0 $_[0]->{stream} = '';
102             }
103              
104             sub _parse_headers {
105 1     1   4 my ($self, @data) = @_;
106 1         2 my @headers = @{ $self->{headers} };
  1         3  
107 1 50       3 unless (@headers) {
108 1 50       5 if (ref $data[0] eq 'ARRAY') {
109 0         0 @headers = @{ shift @data };
  0         0  
110             } else {
111 1         2 @headers = sort keys %{ $data[0] };
  1         6  
112             }
113 1         13 $self->{headers} = \@headers;
114             }
115 1         7 my @body;
116 1         3 for my $d (@data) {
117             push @body, (ref $d eq 'ARRAY')
118             ? $d
119             : [
120             map {
121 2 50       7 $d->{$_}
  6         12  
122             } @headers
123             ];
124             }
125             return (
126 1         4 \@headers,
127             @body
128             );
129             }
130              
131             sub _deparse_headers {
132 1     1   4 my ($self, @data) = @_;
133 1 50       4 return @data unless ref $data[0] eq 'ARRAY';
134 1         2 my @headers = @{ shift @data };
  1         4  
135 1         2 my @body;
136 1         2 for my $d (@data) {
137 2         4 my $i = 0;
138             push @body, (ref $d eq 'HASH')
139             ? $d
140             : {
141             map {
142 2 50       8 $_ => $d->[$i++]
  6         14  
143             } @headers
144             };
145             }
146 1         5 return @body;
147             }
148              
149             1;
150              
151             __END__