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.1';
3             # ABSTRACT: Default renderer for Data::Crumbr
4              
5 6     6   3920 use Mo qw< default coerce >;
  6         10  
  6         36  
6              
7 6     6   1583 use strict;
  6         31  
  6         143  
8 6     6   28 use warnings;
  6         17  
  6         159  
9 6     6   29 use Carp;
  6         8  
  6         374  
10 6     6   38 use English qw< -no_match_vars >;
  6         30  
  6         48  
11 6     6   2814 use Scalar::Util qw< blessed >;
  6         12  
  6         273  
12 6     6   1104 use Data::Crumbr::Util;
  6         10  
  6         4806  
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   10 my ($output) = @_;
38 5   50     39 $output //= [];
39 5         14 my $reftype = ref $output;
40              
41 5 50       45 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       21 if $reftype eq 'GLOB';
58              
59             return sub {
60 85 100   85   777 return $output unless @_;
61 75         292 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 112 my ($self, $stack) = @_;
82              
83 75         181 my $venc = $self->value_encoder();
84 75         795 my @components = $venc->($stack->[-1]{data});
85              
86 75         152 my @keys = map { $_->{encoded} } @$stack;
  295         573  
87 75         123 shift @keys; # first item of @$stack is dummy
88 75         92 pop @keys; # last item of @$stack is the leaf, drop it
89              
90 75         99 my $closers = '';
91 75 50       170 if (@keys) {
92 75         193 unshift @components, join $self->keys_separator(), @keys;
93 75         609 $closers = $stack->[-2]{closers};
94             }
95              
96 75         177 my $record = join $self->value_separator(), @components;
97 75         642 $self->output()->($record . $closers);
98             } ## end sub leaf
99              
100             {
101 6     6   33 no strict 'refs';
  6         11  
  6         2639  
102             *scalar_leaf = \&leaf;
103             *array_leaf = \&leaf;
104             *hash_leaf = \&leaf;
105             }
106              
107             sub array_keys_iterator {
108 10     10 1 19 my ($self, $aref) = @_;
109 10         15 my $i = 0;
110 10         19 my $sup = @$aref;
111             return sub {
112 40 100   40   111 return if $i >= $sup;
113 30         110 return $i++;
114 10         58 };
115             } ## end sub array_keys_iterator
116              
117             sub hash_keys_iterator {
118 15     15 1 28 my ($self, $href) = @_;
119 15         81 my @keys = sort keys %$href; # memory intensive...
120 15     80   106 return sub { return shift @keys };
  80         271  
121             }
122              
123             sub array_key {
124 30     30 1 45 my ($self, $key) = @_;
125 30         112 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 101 my ($self, $key) = @_;
133 65         160 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 12 my ($self) = @_;
141 5 50       17 my $output = $self->output()->()
142             or return;
143 5         42 return join "\n", @$output;
144             } ## end sub result
145              
146             sub reset {
147 5     5 1 10 my ($self) = @_;
148 5 50       22 my $output = $self->output()->()
149             or return;
150 5         12 @$output = ();
151 5         16 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.1
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