File Coverage

blib/lib/Data/Crumbr/Default.pm
Criterion Covered Total %
statement 66 82 80.4
branch 10 32 31.2
condition 1 2 50.0
subroutine 19 22 86.3
pod 7 7 100.0
total 103 145 71.0


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