File Coverage

blib/lib/Data/Crumbr/Default.pm
Criterion Covered Total %
statement 68 84 80.9
branch 10 32 31.2
condition 1 2 50.0
subroutine 20 23 86.9
pod 7 7 100.0
total 106 148 71.6


line stmt bran cond sub pod time code
1             package Data::Crumbr::Default;
2             $Data::Crumbr::Default::VERSION = '0.1.0';
3             # ABSTRACT: Default renderer for Data::Crumbr
4              
5 6     6   3488 use Mo qw< default coerce >;
  6         12  
  6         38  
6              
7 6     6   1532 use 5.018;
  6         19  
8 6     6   26 use strict;
  6         10  
  6         133  
9 6     6   34 use warnings;
  6         32  
  6         143  
10 6     6   26 use Carp;
  6         8  
  6         372  
11 6     6   27 use English qw< -no_match_vars >;
  6         9  
  6         44  
12 6     6   2841 use Scalar::Util qw< blessed >;
  6         13  
  6         798  
13 6     6   1090 use Data::Crumbr::Util;
  6         11  
  6         4531  
14              
15             my $jenc = Data::Crumbr::Util::json_leaf_encoder();
16             my $ienc = Data::Crumbr::Util::id_encoder();
17              
18             has array_open => (default => sub { '' });
19             has array_close => (default => sub { '' });
20             has array_key_prefix => (default => sub { '[' });
21             has array_key_suffix => (default => sub { ']' });
22             has array_key_encoder => (default => sub { $ienc });
23             has hash_open => (default => sub { '' });
24             has hash_close => (default => sub { '' });
25             has hash_key_prefix => (default => sub { '{' });
26             has hash_key_suffix => (default => sub { '}' });
27             has hash_key_encoder => (default => sub { $jenc });
28             has value_encoder => (default => sub { $jenc });
29             has keys_separator => (default => sub { '' });
30             has value_separator => (default => sub { ':' });
31              
32             has output => (
33             default => sub { __output() },
34             coerce => \&__output,
35             );
36              
37             sub __output {
38 5     5   14 my ($output) = @_;
39 5   50     36 $output //= [];
40 5         11 my $reftype = ref $output;
41              
42 5 50       18 if (!$reftype) { # filename, transform into filehandle
43 0         0 my $fh = \*STDOUT;
44 0 0       0 if ($output ne '-') {
45 0         0 $fh = undef;
46 0 0       0 open $fh, '>', $output
47             or croak "open('$output'): $OS_ERROR";
48             }
49 0 0       0 binmode $fh, ':raw'
50             or croak "binmode() on $output: $OS_ERROR";
51 0         0 $reftype = ref($output = $fh);
52             } ## end if (!$reftype)
53              
54             return sub {
55 0 0   0   0 return unless @_;
56 0         0 print {$output} $_[0], "\n";
  0         0  
57             }
58 5 50       16 if $reftype eq 'GLOB';
59              
60             return sub {
61 85 100   85   695 return $output unless @_;
62 75         266 push @$output, $_[0];
63             }
64 5 50       51 if $reftype eq 'ARRAY';
65              
66             return sub {
67 0 0   0   0 return unless @_;
68 0         0 $output->print($_[0]);
69             }
70 0 0       0 if blessed($output);
71              
72             return sub {
73 0 0   0   0 return unless @_;
74 0         0 return $output->($_[0]);
75             }
76 0 0       0 if $reftype eq 'CODE';
77              
78 0         0 croak "invalid output";
79             } ## end sub __output
80              
81             sub leaf {
82 75     75 1 130 my ($self, $stack) = @_;
83              
84 75         160 my $venc = $self->value_encoder();
85 75         579 my @components = $venc->($stack->[-1]{data});
86              
87 75         137 my @keys = map { $_->{encoded} } @$stack;
  295         557  
88 75         110 shift @keys; # first item of @$stack is dummy
89 75         85 pop @keys; # last item of @$stack is the leaf, drop it
90              
91 75         93 my $closers = '';
92 75 50       158 if (@keys) {
93 75         183 unshift @components, join $self->keys_separator(), @keys;
94 75         548 $closers = $stack->[-2]{closers};
95             }
96              
97 75         162 my $record = join $self->value_separator(), @components;
98 75         582 $self->output()->($record . $closers);
99             } ## end sub leaf
100              
101             {
102 6     6   30 no strict 'refs';
  6         17  
  6         2480  
103             *scalar_leaf = \&leaf;
104             *array_leaf = \&leaf;
105             *hash_leaf = \&leaf;
106             }
107              
108             sub array_keys_iterator {
109 10     10 1 16 my ($self, $aref) = @_;
110 10         13 my $i = 0;
111 10         18 my $sup = @$aref;
112             return sub {
113 40 100   40   107 return if $i >= $sup;
114 30         118 return $i++;
115 10         59 };
116             } ## end sub array_keys_iterator
117              
118             sub hash_keys_iterator {
119 15     15 1 22 my ($self, $href) = @_;
120 15         75 my @keys = sort keys %$href; # memory intensive...
121 15     80   92 return sub { return shift @keys };
  80         254  
122             }
123              
124             sub array_key {
125 30     30 1 41 my ($self, $key) = @_;
126 30         67 return join '', $self->array_open(),
127             $self->array_key_prefix(),
128             $self->array_key_encoder()->($key),
129             $self->array_key_suffix();
130             } ## end sub array_key
131              
132             sub hash_key {
133 65     65 1 100 my ($self, $key) = @_;
134 65         141 return join '', $self->hash_open(),
135             $self->hash_key_prefix(),
136             $self->hash_key_encoder()->($key),
137             $self->hash_key_suffix();
138             } ## end sub hash_key
139              
140             sub result {
141 5     5 1 10 my ($self) = @_;
142 5 50       17 my $output = $self->output()->()
143             or return;
144 5         35 return join "\n", @$output;
145             } ## end sub result
146              
147             sub reset {
148 5     5 1 89 my ($self) = @_;
149 5 50       19 my $output = $self->output()->()
150             or return;
151 5         11 @$output = ();
152 5         12 return;
153             } ## end sub reset
154              
155             1;
156              
157             __END__
158              
159             =pod
160              
161             =encoding utf-8
162              
163             =head1 NAME
164              
165             Data::Crumbr::Default - Default renderer for Data::Crumbr
166              
167             =head1 VERSION
168              
169             version 0.1.0
170              
171             =head1 DESCRIPTION
172              
173             This is the default encoder implementation, and most probably the only
174             oney you really need. And most probably, you really not need to directly
175             use it.
176              
177             =head1 INTERFACE
178              
179             =over
180              
181             =item B<< array_key >>
182              
183             returns the encoded array key, optionally opening an array and keeping
184             into account the prefix, the suffix and the encoder for the key
185              
186             =item B<< array_keys_iterator >>
187              
188             returns an iterator sub starting from 0 up to the number of elements in
189             the array
190              
191             =item B<< hash_key >>
192              
193             returns the encoded hash key, optionally opening an hash and keeping
194             into account the prefix, the suffix and the encoder for the key
195              
196             =item B<< hash_keys_iterator >>
197              
198             returns an iterator sub that returns each key in the input hash, sorted
199             lexicographically
200              
201             =item B<< leaf >>
202              
203             =item B<< array_leaf >>
204              
205             =item B<< hash_leaf >>
206              
207             =item B<< scalar_leaf >>
208              
209             this method is called whenever an external iteration component hits a
210             leaf and wants to push a new encoded record to the output
211              
212             =item B<< new >>
213              
214             my $enc = Data::Crumbr::Default->new(%args);
215              
216             create a new encoder object
217              
218             =item B<< reset >>
219              
220             reset the encoder, i.e. wipe out all the internal state to start a new
221             encoding cycle.
222              
223             =item B<< result >>
224              
225             get the outcome of the encoding. Not guaranteed to work.
226              
227             =back
228              
229             =head1 AUTHOR
230              
231             Flavio Poletti <polettix@cpan.org>
232              
233             =head1 COPYRIGHT AND LICENSE
234              
235             Copyright (C) 2015 by Flavio Poletti <polettix@cpan.org>
236              
237             This module is free software. You can redistribute it and/or
238             modify it under the terms of the Artistic License 2.0.
239              
240             This program is distributed in the hope that it will be useful,
241             but without any warranty; without even the implied warranty of
242             merchantability or fitness for a particular purpose.
243              
244             =cut