File Coverage

blib/lib/Attean/API/Parser.pm
Criterion Covered Total %
statement 149 168 88.6
branch 10 20 50.0
condition 5 12 41.6
subroutine 38 41 92.6
pod 19 20 95.0
total 221 261 84.6


line stmt bran cond sub pod time code
1 50     50   648 use v5.14;
  50         158  
2 50     50   268 use warnings;
  50         102  
  50         1747  
3              
4             =head1 NAME
5              
6             Attean::API::Parser - Parser role
7              
8             =head1 VERSION
9              
10             This document describes Attean::API::Parser version 0.032
11              
12             =head1 DESCRIPTION
13              
14             The Attean::API::Parser role defines a common API for all parsers of typed
15             objects from data (either a byte string or a filehandle).
16              
17             =head1 ATTRIBUTES
18              
19             The following attributes exist:
20              
21             =over 4
22              
23             =item C<< handler >>
24              
25             A code reference that will be called during callback-variant parsing methods.
26             This attribute has a default (no-op function), so specifying it is not
27             necessary if using iterator- or list-variant parsing methods.
28              
29             =back
30              
31             =head1 REQUIRED METHODS
32              
33             The following methods are required by the L<Attean::API::Parser> role:
34              
35             =over 4
36              
37             =item C<< canonical_media_type >>
38              
39             Returns the canonical media type string for the format of this parser.
40              
41             =item C<< media_types >>
42              
43             Returns an ARRAY reference of media type strings that are acceptable as input
44             to this parser.
45              
46             =item C<< handled_type >>
47              
48             Returns a L<Type::Tiny> object representing the type of items that result from
49             parsing.
50              
51             =item C<< file_extensions >>
52              
53             Returns an ARRAY reference of file extensions commonly associated with the
54             media types supported by the parser (and returned by C<< media_types >>).
55             File extensions should NOT include a leading dot.
56              
57             =cut
58              
59 50     50   271 use Type::Tiny::Role;
  50         97  
  50         2373  
60              
61             use Types::Standard qw(CodeRef Bool);
62 50     50   306  
  50         120  
  50         483  
63             use Moo::Role;
64 50     50   33682 use namespace::clean;
  50         104  
  50         286  
65 50     50   16646
  50         114  
  50         501  
66             has 'handler' => (is => 'rw', isa => CodeRef, default => sub { sub {} });
67             has 'lazy_iris' => (is => 'rw', isa => Bool, default => 0);
68            
69             requires 'canonical_media_type'; # => (is => 'ro', isa => 'Str', init_arg => undef);
70             requires 'media_types'; # => (is => 'ro', isa => 'ArrayRef[Str]', init_arg => undef);
71             requires 'handled_type'; # => (is => 'ro', isa => 'Type::Tiny', init_arg => undef);
72             requires 'file_extensions'; # => (is => 'ro', isa => 'ArrayRef[Str]', init_arg => undef);
73            
74             =item C<< new_iri( value => $value ) >>
75              
76             Constructs and returns a new L<Attean::IRI> object, respecting the parser's
77             C<lazy_iris> attribute.
78             =cut
79              
80             my $self = shift;
81             my %args;
82 306     306 1 522 if ($self->lazy_iris) {
83 306         451 $args{lazy} = 1;
84 306 50       5312 } else {
85 0         0 $args{lazy} = 0;
86             }
87 306         2121 if (scalar(@_) == 1) {
88             $args{value} = shift;
89 306 100       669 } else {
90 103         234 %args = (%args, @_);
91             }
92 203         782 return Attean::IRI->new(%args);
93             }
94 306         4886
95             }
96              
97 0     0 1 0 use Types::Standard qw(ConsumerOf InstanceOf Maybe);
98             use Types::Namespace qw( NamespaceMap );
99             use Scalar::Util qw(blessed);
100              
101 50     50   42034 use Moo::Role;
  50         123  
  50         251  
102 50     50   31617
  50         107  
  50         365  
103 50     50   16275 with 'Attean::API::Parser';
  50         104  
  50         2109  
104             has 'base' => (is => 'rw', isa => ConsumerOf['Attean::API::IRI'], coerce => sub { blessed($_[0]) ? Attean::IRI->new($_[0]->as_string) : Attean::IRI->new($_[0]) }, predicate => 'has_base');
105 50     50   256 has 'namespaces' => (is => 'ro', isa => Maybe[NamespaceMap]);
  50         101  
  50         224  
106             }
107              
108             use Moo::Role;
109             with 'Attean::API::Parser';
110              
111             requires 'parse_cb_from_io'; # parse_cb_from_io($io)
112             requires 'parse_cb_from_bytes'; # parse_cb_from_bytes($data)
113 50     50   22257
  50         129  
  50         267  
114             my $self = shift;
115             my @values = $self->parse_list_from_io(@_);
116             if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) {
117             my %vars;
118             foreach my $r (@values) {
119             if ($r->does('Attean::API::Result')) {
120 4     4 1 145 foreach my $v ($r->variables) {
121 4         21 $vars{$v}++;
122 4 50 33     19 }
123 0         0 }
124 0         0 }
125 0 0       0 return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, );
126 0         0 } else {
127 0         0 return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, );
128             }
129             }
130            
131 0         0 my $self = shift;
132             my @values = $self->parse_list_from_bytes(@_);
133 4         183 if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) {
134             my %vars;
135             foreach my $r (@values) {
136             if ($r->does('Attean::API::Result')) {
137             foreach my $v ($r->variables) {
138 29     29 1 4253 $vars{$v}++;
139 29         144 }
140 29 100 66     125 }
141 1         41 }
142 1         2 return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, );
143 2 50       6 } else {
144 2         28 return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, );
145 6         10 }
146             }
147            
148             my $self = shift;
149 1         8 my @values;
150             $self->handler(sub {
151 28         1312 push(@values, shift);
152             });
153             $self->parse_cb_from_io(@_);
154             return @values;
155             }
156 5     5 1 94
157 5         13 my $self = shift;
158             my @values;
159 9     9   80 $self->handler(sub {
160 5         114 push(@values, shift);
161 5         170 });
162 5         24 $self->parse_cb_from_bytes(@_);
163             return @values;
164             }
165             }
166 33     33 1 281  
167 33         73 use Moo::Role;
168             with 'Attean::API::Parser';
169 91     91   771
170 33         749 requires 'parse_iter_from_io'; # $iter = parse_iter_from_io($io)
171 33         1105 requires 'parse_iter_from_bytes'; # $iter = parse_iter_from_bytes($data)
172 32         148
173             my $self = shift;
174             my $io = shift;
175             my $handler = $self->handler;
176             my $iter = $self->parse_iter_from_io($io);
177 50     50   37601 while (my $item = $iter->next) { $handler->( $item ) }
  50         129  
  50         259  
178             }
179            
180             my $self = shift;
181             my $data = shift;
182             my $handler = $self->handler;
183             my $iter = $self->parse_iter_from_bytes($data);
184 1     1 1 65 while (defined(my $item = $iter->next)) { $handler->( $item ) }
185 1         2 }
186 1         19
187 1         9 my $self = shift;
188 1         41 my $io = shift;
  2         18  
189             my $iter = $self->parse_iter_from_io($io);
190             my @list;
191             while (defined(my $item = $iter->next)) { push(@list, $item); }
192 2     2 1 1615 return @list;
193 2         6 }
194 2         34
195 2         20 my $self = shift;
196 2         89 my $data = shift;
  6         22  
197             my $iter = $self->parse_iter_from_bytes($data);
198             my @list;
199             while (defined(my $item = $iter->next)) { push(@list, $item); }
200 2     2 1 1802 return @list;
201 2         4 }
202 2         11 }
203 2         66  
204 2         9 use Moo::Role;
  4         15  
205 2         27 with 'Attean::API::Parser';
206            
207             requires 'parse_list_from_io'; # @list = parse_list_from_io($io)
208             requires 'parse_list_from_bytes'; # @list = parse_list_from_bytes($data)
209 14     14 1 5538
210 14         25 my $self = shift;
211 14         55 my $io = shift;
212 14         284 my $handler = $self->handler;
213 14         57 my $iter = $self->parse_iter_from_io($io);
  18         499  
214 12         167 while (my $item = $iter->next) { $handler->( $item ) }
215             }
216            
217             my $self = shift;
218             my $data = shift;
219 50     50   28626 my $handler = $self->handler;
  50         114  
  50         221  
220             my $iter = $self->parse_iter_from_bytes($data);
221             while (defined(my $item = $iter->next)) { $handler->( $item ) }
222             }
223            
224             my $self = shift;
225             my @values = $self->parse_list_from_io(@_);
226 1     1 1 204 if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) {
227 1         3 my %vars;
228 1         17 foreach my $r (@values) {
229 1         13 if ($r->does('Attean::API::Result')) {
230 1         28 foreach my $v ($r->variables) {
  1         4  
231             $vars{$v}++;
232             }
233             }
234 1     1 1 65 }
235 1         2 return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, );
236 1         19 } else {
237 1         9 return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, );
238 1         34 }
  1         4  
239             }
240            
241             my $self = shift;
242 2     2 1 75 my @values = $self->parse_list_from_bytes(@_);
243 2         10 if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) {
244 2 50 33     8 my %vars;
245 0         0 foreach my $r (@values) {
246 0         0 if ($r->does('Attean::API::Result')) {
247 0 0       0 foreach my $v ($r->variables) {
248 0         0 $vars{$v}++;
249 0         0 }
250             }
251             }
252             return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, );
253 0         0 } else {
254             return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, );
255 2         92 }
256             }
257            
258             }
259              
260 1     1 1 2 # Parser returns objects that conform to Attean::API::Term
261 1         6 use Moo::Role;
262 1 50 33     4 with 'Attean::API::Parser';
263 1         36 state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Term');
264 1         3 return $ITEM_TYPE;
265 1 50       4 }
266 1         17 }
267 5         9  
268             # Parser returns objects that conform to Attean::API::Triple
269             use Moo::Role;
270             with 'Attean::API::Parser';
271 1         6 state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Triple');
272             return $ITEM_TYPE;
273 0         0 }
274             }
275              
276             # Parser returns objects that conform to Attean::API::Quad
277             use Moo::Role;
278             with 'Attean::API::Parser';
279             state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Quad');
280             return $ITEM_TYPE;
281 50     50   36880 }
  50         151  
  50         259  
282             }
283              
284 0     0 1 0 # Parser returns objects that conform to either Attean::API::Triple or Attean::API::Quad
285 0         0 use Moo::Role;
286             with 'Attean::API::Parser';
287             state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::TripleOrQuad');
288             return $ITEM_TYPE;
289             }
290             }
291 50     50   18803  
  50         117  
  50         249  
292             # Parser returns objects that conform to either Attean::API::Result or Attean::API::Term
293             use Moo::Role;
294 50     50 1 978 with 'Attean::API::Parser';
295 50         2008 state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::ResultOrTerm');
296             return $ITEM_TYPE;
297             }
298             }
299              
300             # Parser returns objects that conform to Attean::API::Result
301 50     50   18213 use Moo::Role;
  50         138  
  50         251  
302             with 'Attean::API::Parser';
303             state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Result');
304 0     0 1 0 return $ITEM_TYPE;
305 0         0 }
306             }
307              
308             1;
309              
310              
311 50     50   19068 =back
  50         122  
  50         195  
312              
313             =head1 BUGS
314 10     10 1 392  
315 10         409 Please report any bugs or feature requests to through the GitHub web interface
316             at L<https://github.com/kasei/attean/issues>.
317              
318             =head1 SEE ALSO
319              
320              
321 50     50   19249  
  50         157  
  50         248  
322             =head1 AUTHOR
323              
324 8     8 0 801 Gregory Todd Williams C<< <gwilliams@cpan.org> >>
325 8         482  
326             =head1 COPYRIGHT
327              
328             Copyright (c) 2014--2022 Gregory Todd Williams.
329             This program is free software; you can redistribute it and/or modify it under
330             the same terms as Perl itself.
331 50     50   18194  
  50         140  
  50         246  
332             =cut