File Coverage

blib/lib/JSON/Lines.pm
Criterion Covered Total %
statement 100 112 89.2
branch 30 50 60.0
condition 4 6 66.6
subroutine 18 19 94.7
pod 10 10 100.0
total 162 197 82.2


line stmt bran cond sub pod time code
1             package JSON::Lines;
2 8     8   471095 use 5.006; use strict; use warnings; our $VERSION = '1.00';
  8     8   82  
  8     8   36  
  8         10  
  8         155  
  8         33  
  8         20  
  8         360  
3 8     8   4623 use JSON; use base 'Import::Export';
  8     8   86730  
  8         41  
  8         1004  
  8         16  
  8         3490  
4              
5             our ($JSON, $LINES, %EX);
6             BEGIN {
7 8     8   120453 $JSON = JSON->new;
8 8         55 $LINES = qr{ ([\[\{] (?: (?> [^\[\]\{\}]+ ) | (??{ $LINES }) )* [\]\}]) }x;
9 8         8493 %EX = (
10             jsonl => [qw/all/]
11             );
12             }
13              
14             sub jsonl {
15 4 50   4 1 2887 my %args = (scalar @_ == 1 ? %{$_[0]} : @_);
  0         0  
16 4         16 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       14 if ($args{encode});
    100          
21             return $args{file}
22             ? $self->decode_file($args{file})
23             : $self->decode($args{data})
24 2 50       10 if ($args{decode});
    50          
25             }
26              
27             sub new {
28 10 50   10 1 497 my ($pkg, %args) = (shift, scalar @_ == 1 ? %{$_[0]} : @_);
  0         0  
29 10         37 my $self = bless { headers => [] }, $pkg;
30 10   66     142 exists $args{$_} && $JSON->$_($args{$_}) for qw/json pretty canonical/;
31 10         81 $self->{$_} = $args{$_} for qw/parse_headers error_cb success_cb/;
32 10         27 $self;
33             }
34              
35             sub encode {
36 11 100   11 1 2316 my ($self, @data) = (shift, scalar @_ == 1 ? @{$_[0]} : @_);
  7         16  
37             @data = $self->_parse_headers(@data)
38 11 100       36 if ($self->{parse_headers});
39 11         14 my $stream;
40 11         21 for (@data) {
41 28         42 my $json = eval { $JSON->encode($_) };
  28         177  
42 28 50       61 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       65 $self->{success_cb}->($json, $_) if $self->{success_cb};
50 28 100       125 $stream .= $json . ($json =~ m/\n$/ ? "" : "\n");
51             }
52             }
53 11         55 return $self->{stream} = $stream;
54             }
55              
56             sub encode_file {
57 1     1 1 19 my ($self, $file) = (shift, shift);
58 1         105 open my $fh, '>', $file;
59 1         7 print $fh $self->encode(@_);
60 1         73 close $fh;
61 1         8 return $file;
62             }
63              
64             sub decode {
65 9     9 1 3955 my ($self, $string) = @_;
66 9         44 my @lines;
67             push @lines, $self->_decode_line($_)
68 9         126 for ($string =~ m/$LINES/g);
69             @lines = $self->_deparse_headers(@lines)
70 9 100       37 if ($self->{parse_headers});
71 9 100       51 return wantarray ? @lines : \@lines;
72             }
73              
74             sub decode_file {
75 1     1 1 736 my ($self, $file) = (shift, shift);
76 1         34 open my $fh, '<', $file;
77 1         3 my $content = do { local $/; <$fh> };
  1         4  
  1         29  
78 1         9 close $fh;
79 1         5 return $self->decode($content);
80             }
81              
82             sub add_line {
83 2     2 1 1127 my ($self, $line, $fh) = @_;
84 2 50       9 if (defined $fh) {
85 0         0 print $fh $self->encode([$line]);
86             } else {
87 2         5 my $stream = $self->{stream};
88 2         6 my $add = $self->encode([$line]);
89 2         6 $self->{stream} = $stream . $add;
90 2         7 $self->{stream};
91             }
92             }
93              
94             sub clear_stream {
95 0     0 1 0 $_[0]->{stream} = '';
96             }
97              
98             sub get_lines {
99 1     1 1 1013 my ($self, $fh, $lines) = @_;
100 1         2 my @lines;
101 1         5 for (1 .. $lines) {
102 3         8 my $line = $self->get_line($fh);
103 3         5 push @lines, $line;
104 3 100       16 last if eof($fh);
105             }
106 1 50       8 return wantarray ? @lines : \@lines;
107             }
108              
109             sub get_line {
110 6     6 1 3239 my ($self, $fh) = @_;
111 6         10 my $line = '';
112 6   66     321 $line .= <$fh> while ($line !~ m/^$LINES/ && !eof($fh));
113 6         22 return $self->_decode_line($line);
114             }
115              
116             sub _decode_line {
117 32     32   75 my ($self, $line) = @_;
118 32         35 my $struct = eval { $JSON->decode($line) };
  32         203  
119 32 50       83 if ($@) {
120 0 0       0 if ($self->{error_cb}) {
121 0         0 return $self->{error_cb}->($@, $line);
122             } else {
123 0         0 die $@;
124             }
125             }
126 32 50       67 return $self->{success_cb}->($struct, $line) if $self->{success_cb};
127 32         72 return $struct;
128             }
129              
130             sub _parse_headers {
131 1     1   3 my ($self, @data) = @_;
132 1         1 my @headers = @{ $self->{headers} };
  1         3  
133 1 50       3 unless (@headers) {
134 1 50       3 if (ref $data[0] eq 'ARRAY') {
135 0         0 @headers = @{ shift @data };
  0         0  
136             } else {
137 1         2 @headers = sort keys %{ $data[0] };
  1         7  
138             }
139 1         9 $self->{headers} = \@headers;
140             }
141 1         5 my @body;
142 1         2 for my $d (@data) {
143             push @body, (ref $d eq 'ARRAY')
144             ? $d
145             : [
146             map {
147 2 50       5 $d->{$_}
  6         10  
148             } @headers
149             ];
150             }
151             return (
152 1         3 \@headers,
153             @body
154             );
155             }
156              
157             sub _deparse_headers {
158 1     1   4 my ($self, @data) = @_;
159 1 50       3 return @data unless ref $data[0] eq 'ARRAY';
160 1         2 my @headers = @{ shift @data };
  1         3  
161 1         2 my @body;
162 1         2 for my $d (@data) {
163 2         3 my $i = 0;
164             push @body, (ref $d eq 'HASH')
165             ? $d
166             : {
167             map {
168 2 50       5 $_ => $d->[$i++]
  6         14  
169             } @headers
170             };
171             }
172 1         4 return @body;
173             }
174              
175             1;
176              
177             __END__