File Coverage

blib/lib/XML/Loy/Atom.pm
Criterion Covered Total %
statement 183 192 95.3
branch 84 104 80.7
condition 20 30 66.6
subroutine 34 34 100.0
pod 19 19 100.0
total 340 379 89.7


line stmt bran cond sub pod time code
1             package XML::Loy::Atom;
2 9     9   77571 use Carp qw/carp/;
  9         28  
  9         525  
3 9     9   55 use strict;
  9         16  
  9         189  
4 9     9   40 use warnings;
  9         16  
  9         282  
5 9     9   3024 use Mojo::ByteStream 'b';
  9         1198486  
  9         517  
6 9     9   4743 use XML::Loy::Date::RFC3339;
  9         25  
  9         573  
7              
8             # Todo:
9             # - see http://search.cpan.org/dist/XML-Atom-SimpleFeed
10              
11             our @CARP_NOT;
12              
13             # Make it an XML::Loy base class
14 9         61 use XML::Loy with => (
15             mime => 'application/atom+xml',
16             prefix => 'atom',
17             namespace => 'http://www.w3.org/2005/Atom'
18 9     9   4459 );
  9         26  
19              
20              
21             # Namespace declaration
22             state $XHTML_NS = 'http://www.w3.org/1999/xhtml';
23              
24              
25             # New person construct
26             sub new_person {
27 3     3 1 2118 my $self = shift;
28 3         18 my $person = ref($self)->SUPER::new('person');
29              
30 3         13 my %hash = @_;
31 3         18 $person->set($_ => $hash{$_}) foreach keys %hash;
32 3         16 return $person;
33             };
34              
35              
36             # New text construct
37             sub new_text {
38 47     47 1 6670 my $self = shift;
39              
40 47 50       91 return unless $_[0];
41              
42 47         71 my $class = ref($self);
43              
44             # Expect empty html
45 47 100       85 unless (defined $_[1]) {
46 5         60 return $class->SUPER::new(
47             text => {
48             type => 'text',
49             -type => 'raw'
50             } => shift );
51             };
52              
53 42         67 my ($type, $content, %hash);
54              
55             # Only textual content
56 42 100 100     246 if (!defined $_[2] && $_[0] =~ m/(?:text|x?html)/) {
    50          
57 21         54 $type = shift;
58 21         31 $content = shift;
59             }
60              
61             # Hash definition
62             elsif ((@_ % 2) == 0) {
63 21         67 %hash = @_;
64              
65 21   100     63 $type = delete $hash{type} || 'text';
66              
67 21 50       46 if (exists $hash{src}) {
68 0         0 return $class->SUPER::new(
69             text => { type => $type, %hash }
70             );
71             };
72              
73 21 100       64 $content = delete $hash{content} or return;
74             };
75              
76             # Content node
77 41         83 my $c_node;
78              
79             # xhtml
80 41 100 100     207 if ($type eq 'xhtml') {
    100          
    50          
81              
82             # Create new by hash
83 11         60 $c_node = $class->SUPER::new(
84             text => {
85             type => $type,
86             %hash
87             });
88              
89             # XHTML content - allowed to be pretty printed
90 11         40 $c_node->add(
91             -div => {
92             xmlns => $XHTML_NS
93             })->append_content($content);
94             }
95              
96             # html or text
97             elsif ($type eq 'html' || $type =~ /^text/i) {
98              
99             # Content is raw and thus nonindented
100 25         169 $c_node = $class->new(
101             text => {
102             'type' => $type,
103             '-type' => 'raw',
104             'xml:space' => 'preserve',
105             %hash
106             } => $content . ''
107             );
108             }
109              
110             # xml media type
111             elsif ($type =~ /[\/\+]xml(;.+)?$/i) {
112 0         0 $c_node = $class->new(
113             text => {
114             type => $type,
115             -type => 'raw',
116             %hash
117             } => $content);
118             }
119              
120             # all other media types
121             else {
122 5         32 $c_node = $class->new(
123             text => {
124             type => $type,
125             -type => 'armour',
126             %hash
127             },
128             $content);
129             };
130              
131 41         2237 return $c_node;
132             };
133              
134              
135             # Add author information
136             sub author {
137 25     25 1 2965 my $self = shift;
138              
139             # Add author
140 25 100       89 return $self->_add_person(author => @_) if $_[0];
141              
142             # Get author information
143 11         30 return $self->_get_information_array('author');
144             };
145              
146              
147             # Add category information
148             sub category {
149 5     5 1 1135 my $self = shift;
150              
151             # Set category
152 5 100       15 if ($_[0]) {
153 2 100       5 if (!defined $_[1]) {
154 1         6 return $self->add(category => { term => shift });
155             };
156              
157 1         6 return $self->add(category => { @_ } );
158             };
159              
160             # Get category
161 3 50       9 my $coll = $self->_get_information_array('category')
162             or return;
163              
164 3 50       20 if ($coll->[0]) {
165 3     6   24 $coll->map(sub { $_ = $_->{term} });
  6         100  
166             };
167              
168 3         78 return $coll;
169             };
170              
171              
172             # Add contributor information
173             sub contributor {
174 6     6 1 2233 my $self = shift;
175              
176             # Add contributor
177 6 100       17 return $self->_add_person(contributor => @_) if $_[0];
178              
179             # Get contributor information
180 3         10 return $self->_get_information_array('contributor');
181             };
182              
183              
184             # Add content information
185             sub content {
186 18     18 1 6064 my $self = shift;
187              
188             # Set content
189 18 100       65 return $self->_addset_text(set => content => @_) if $_[0];
190              
191             # Return content
192 3         9 return $self->_get_information_single('content');
193             };
194              
195              
196             # Set or get entry
197             sub entry {
198 15     15 1 6573 my $self = shift;
199              
200             # Is object
201 15 50 100     109 if (ref $_[0]) {
    100          
202 0         0 return $self->add(@_);
203             }
204              
205             # Get entry
206             elsif ($_[0] && !$_[1]) {
207              
208 5         8 my $id = shift;
209              
210             # Get based on xml:id
211 5         23 my $entry = $self->at(qq{entry[xml\\:id="$id"]});
212 5 100       980 return $entry if $entry;
213              
214             # Get based on id
215 2     6   16 my $idc = $self->find('entry > id')->grep(sub { $_->text eq $id });
  6         192  
216              
217 2 50 33     71 return unless $idc && $idc->[0];
218              
219 2         24 return $idc->[0]->parent;
220             };
221              
222 10         32 my %hash = @_;
223 10         18 my $entry;
224              
225             # Set id additionally as xml:id
226 10 100       34 if (exists $hash{id}) {
227             $entry = $self->add(
228             entry => {'xml:id' => $hash{id}}
229 8         54 );
230             }
231              
232             # No id given
233             else {
234 2         6 $entry = $self->add('entry');
235             };
236              
237             # Add information
238 10         38 foreach (keys %hash) {
239 9         27 $entry->add($_, $hash{$_});
240             };
241              
242 10         60 return $entry;
243             };
244              
245              
246             # Set or get generator information
247             sub generator {
248 4     4 1 2174 shift->_simple_feed_info(generator => @_);
249             };
250              
251              
252             # Set or get icon information
253             sub icon {
254 3     3 1 1483 shift->_simple_feed_info(icon => @_);
255             };
256              
257              
258             # Add id
259             sub id {
260 5     5 1 710 my $self = shift;
261              
262             # Get id
263 5 100       14 unless ($_[0]) {
264 2         7 my $id_obj = $self->_get_information_single('id');
265 2 50       12 return $id_obj->text if $id_obj;
266 0         0 return;
267             };
268              
269 3         5 my $id = shift;
270 3         13 my $element = $self->set(id => $id);
271 3 50       9 return unless $element;
272              
273             # Add xml:id also
274 3         26 $element->parent->attr('xml:id' => $id);
275 3         132 return $self;
276             };
277              
278              
279             # Add link information
280             sub link {
281 19     19 1 2138 my $self = shift;
282              
283 19 100       47 if ($_[1]) {
284              
285             # rel => href
286 11 100       30 if (@_ == 2) {
287 5         25 return $self->add(link => {
288             rel => shift,
289             href => shift
290             });
291             };
292              
293             # Parameter
294 6         23 my %values = @_;
295             # href, rel, type, hreflang, title, length
296 6   50     31 my $rel = delete $values{rel} || 'related';
297 6         50 return $self->add(link => {
298             rel => $rel,
299             %values
300             });
301             };
302              
303 8         16 my $rel = shift;
304              
305 8         10 my $children;
306             # Node is root
307 8 100       23 unless ($self->parent) {
308 5         84 $children = $self->at('*')->children('link');
309             }
310              
311             # Node is under root
312             else {
313 3         85 $children = $self->children('link');
314             };
315              
316 8     16   93 return $children->grep(sub { $_->attr('rel') eq $rel });
  16         224  
317             };
318              
319              
320             # Add logo
321             sub logo {
322 3     3 1 2145 shift->_simple_feed_info(logo => @_);
323             };
324              
325              
326             # Add publish time information
327             sub published {
328 14     14 1 2149 shift->_date(published => @_);
329             };
330              
331              
332             # Add rights information
333             sub rights {
334 3     3 1 1455 my $self = shift;
335              
336             # Set rights
337 3 100       11 return $self->_addset_text(set => rights => @_) if $_[0];
338              
339             # Return rights
340 1         4 return $self->_get_information_single('rights');
341             };
342              
343              
344             # Add source information to entry
345             sub source {
346 3     3 1 2210 my $self = shift;
347              
348             # Only valid in entry
349 3 50 33     11 return if !$self->tag || $self->tag ne 'entry';
350              
351             # Set source
352 3 100       97 return $self->set(source => @_) if $_[0];
353              
354             # Return source
355 2         8 return $self->_get_information_single('source');
356             };
357              
358              
359             # Add subtitle
360             sub subtitle {
361 10     10 1 3441 my $self = shift;
362              
363             # Only valid in feed or source or something
364 10 100 66     25 return if $self->tag && $self->tag eq 'entry';
365              
366             # Set subtitle
367 9 100       152 return $self->_addset_text(set => subtitle => @_) if $_[0];
368              
369             # Return subtitle
370 3         9 return $self->_get_information_single('subtitle');
371             };
372              
373              
374             # Add summary
375             sub summary {
376 12     12 1 3384 my $self = shift;
377              
378             # Only valid in entry
379 12 100 66     34 return if !$self->tag || $self->tag ne 'entry';
380              
381             # Set summary
382 11 100       336 return $self->_addset_text(set => summary => @_) if $_[0];
383              
384             # Return summary
385 5         20 return $self->_get_information_single('summary');
386             };
387              
388              
389             # Add title
390             sub title {
391 15     15 1 3902 my $self = shift;
392              
393             # Set title
394 15 100       73 return $self->_addset_text(set => title => @_) if $_[0];
395              
396             # Return title
397 6         30 return $self->_get_information_single('title');
398             };
399              
400              
401             # Add update time information
402             sub updated {
403 7     7 1 1221 shift->_date(updated => @_);
404             };
405              
406              
407             # Add person information
408             sub _add_person {
409 17     17   40 my $self = shift;
410 17         28 my $type = shift;
411              
412             # Person is a defined node
413 17 100       42 if (ref($_[0])) {
414 3         9 my $person = shift;
415 3         15 $person->root->at('*')->tree->[1] = $type;
416 3         113 return $self->add($person);
417             }
418              
419             # Person is a hash
420             else {
421 14         71 my $person = $self->add($type);
422 14         55 my %data = @_;
423              
424 14         52 foreach (keys %data) {
425 15 50       81 $person->add($_ => $data{$_} ) if $data{$_};
426             };
427 14         69 return $person;
428             };
429             };
430              
431              
432             # Add date construct
433             sub _date {
434 21     21   41 my $self = shift;
435 21         30 my $type = shift;
436              
437             # Set date
438 21 100       47 if ($_[0]) {
439 9         14 my $date = shift;
440              
441 9 50       22 unless (ref($date)) {
442 9         43 $date = XML::Loy::Date::RFC3339->new($date);
443             };
444              
445 9         65 return $self->set($type, $date->to_string);
446             };
447              
448             # Get published information
449 12         31 my $date = $self->_get_information_single($type);
450              
451             # Parse date
452 12 50       83 return XML::Loy::Date::RFC3339->new($date->text) if $date;
453              
454             # No publish information found
455 0         0 return;
456             };
457              
458              
459             # Add text information
460             sub _addset_text {
461 73     73   137 my $self = shift;
462 73         94 my $action = shift;
463              
464 73 50 33     289 unless ($action eq 'add' || $action eq 'set') {
465 0 0       0 warn 'Action has to be set or add' and return;
466             };
467              
468 73         98 my $type = shift;
469              
470             # Text is a defined node
471 73 100       143 if (ref $_[0]) {
472              
473 37         45 my $text = shift;
474              
475             # Get root element
476 37         117 my $root_elem = $text->root->at('*');
477              
478 37         660 $root_elem->tree->[1] = $type;
479 37         331 my $root_att = $root_elem->attr;
480              
481             # Delete type
482 37   50     495 my $c_type = $root_att->{type} || '';
483 37 100       86 if ($c_type eq 'text') {
484 16         29 delete $root_elem->attr->{'type'};
485             };
486              
487 37         248 $text->root->at('*')->tree->[1] = $type;
488              
489 37         885 my $element = $self->$action($text);
490              
491             # Return wrapped div
492 37 100       465 return $element->at('div') if $c_type eq 'xhtml';
493              
494             # Return node
495 28         179 return $element;
496             };
497              
498 36         44 my $text;
499             # Text is no hash
500 36 100       74 unless (defined $_[1]) {
501 12         31 $text = $self->new_text(
502             type => 'text',
503             content => shift
504             );
505             }
506              
507             # Text is a hash
508             else {
509 24         66 $text = $self->new_text(@_);
510             };
511              
512             # Todo: Optimize!
513 36 100       151 return $self->_addset_text($action, $type, $text) if ref $text;
514 1         5 return;
515             };
516              
517              
518             # Return information of entries or the feed
519             sub _get_information_array {
520 17     17   29 my $self = shift;
521 17         26 my $type = shift;
522              
523             # Get author objects
524 17         56 my $children = $self->children($type);
525              
526             # Return information of object
527 17 50       128 return $children if $children->[0];
528              
529             # Return feed information
530 0         0 return $self->find('feed > ' . $type);
531             };
532              
533              
534             # Return information of entries or the feed
535             sub _get_information_single {
536 34     34   57 my $self = shift;
537 34         45 my $type = shift;
538              
539             # Get author objects
540 34         95 my $children = $self->children($type);
541              
542             # Return information of object
543 34 50       248 return $children->[0] if $children->[0];
544              
545             # Return feed information
546 0         0 return $self->at('feed > ' . $type);
547             };
548              
549              
550             # Get or set simple feed information
551             # like generator or icon
552             sub _simple_feed_info {
553 10     10   20 my $self = shift;
554 10         14 my $type = shift;
555              
556 10         28 my $feed = $self->root->at('feed');
557 10 100       677 return unless $feed;
558              
559             # Set
560 7 100       42 if ($_[0]) {
561 6         14 return $feed->set($type => @_);
562             };
563              
564             # Get generator information
565 1         4 my $gen = $feed->at($type);
566 1 50       20 return $gen->all_text if $gen;
567 0           return;
568             };
569              
570              
571             1;
572              
573              
574             __END__