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   78518 use Carp qw/carp/;
  9         27  
  9         447  
3 9     9   44 use strict;
  9         13  
  9         160  
4 9     9   36 use warnings;
  9         12  
  9         248  
5 9     9   2616 use Mojo::ByteStream 'b';
  9         1030411  
  9         420  
6 9     9   4054 use XML::Loy::Date::RFC3339;
  9         22  
  9         468  
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         49 use XML::Loy with => (
15             mime => 'application/atom+xml',
16             prefix => 'atom',
17             namespace => 'http://www.w3.org/2005/Atom'
18 9     9   3846 );
  9         24  
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 2101 my $self = shift;
28 3         17 my $person = ref($self)->SUPER::new('person');
29              
30 3         11 my %hash = @_;
31 3         18 $person->set($_ => $hash{$_}) foreach keys %hash;
32 3         14 return $person;
33             };
34              
35              
36             # New text construct
37             sub new_text {
38 47     47 1 6124 my $self = shift;
39              
40 47 50       89 return unless $_[0];
41              
42 47         67 my $class = ref($self);
43              
44             # Expect empty html
45 47 100       83 unless (defined $_[1]) {
46 5         35 return $class->SUPER::new(
47             text => {
48             type => 'text',
49             -type => 'raw'
50             } => shift );
51             };
52              
53 42         58 my ($type, $content, %hash);
54              
55             # Only textual content
56 42 100 100     227 if (!defined $_[2] && $_[0] =~ m/(?:text|x?html)/) {
    50          
57 21         41 $type = shift;
58 21         28 $content = shift;
59             }
60              
61             # Hash definition
62             elsif ((@_ % 2) == 0) {
63 21         68 %hash = @_;
64              
65 21   100     51 $type = delete $hash{type} || 'text';
66              
67 21 50       44 if (exists $hash{src}) {
68 0         0 return $class->SUPER::new(
69             text => { type => $type, %hash }
70             );
71             };
72              
73 21 100       57 $content = delete $hash{content} or return;
74             };
75              
76             # Content node
77 41         68 my $c_node;
78              
79             # xhtml
80 41 100 100     182 if ($type eq 'xhtml') {
    100          
    50          
81              
82             # Create new by hash
83 11         56 $c_node = $class->SUPER::new(
84             text => {
85             type => $type,
86             %hash
87             });
88              
89             # XHTML content - allowed to be pretty printed
90 11         45 $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         164 $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         28 $c_node = $class->new(
123             text => {
124             type => $type,
125             -type => 'armour',
126             %hash
127             },
128             $content);
129             };
130              
131 41         1918 return $c_node;
132             };
133              
134              
135             # Add author information
136             sub author {
137 25     25 1 3027 my $self = shift;
138              
139             # Add author
140 25 100       96 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 1197 my $self = shift;
150              
151             # Set category
152 5 100       16 if ($_[0]) {
153 2 100       5 if (!defined $_[1]) {
154 1         6 return $self->add(category => { term => shift });
155             };
156              
157 1         7 return $self->add(category => { @_ } );
158             };
159              
160             # Get category
161 3 50       8 my $coll = $self->_get_information_array('category')
162             or return;
163              
164 3 50       21 if ($coll->[0]) {
165 3     6   26 $coll->map(sub { $_ = $_->{term} });
  6         82  
166             };
167              
168 3         76 return $coll;
169             };
170              
171              
172             # Add contributor information
173             sub contributor {
174 6     6 1 2215 my $self = shift;
175              
176             # Add contributor
177 6 100       18 return $self->_add_person(contributor => @_) if $_[0];
178              
179             # Get contributor information
180 3         7 return $self->_get_information_array('contributor');
181             };
182              
183              
184             # Add content information
185             sub content {
186 18     18 1 4979 my $self = shift;
187              
188             # Set content
189 18 100       61 return $self->_addset_text(set => content => @_) if $_[0];
190              
191             # Return content
192 3         10 return $self->_get_information_single('content');
193             };
194              
195              
196             # Set or get entry
197             sub entry {
198 15     15 1 6041 my $self = shift;
199              
200             # Is object
201 15 50 100     99 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       926 return $entry if $entry;
213              
214             # Get based on id
215 2     6   7 my $idc = $self->find('entry > id')->grep(sub { $_->text eq $id });
  6         189  
216              
217 2 50 33     71 return unless $idc && $idc->[0];
218              
219 2         19 return $idc->[0]->parent;
220             };
221              
222 10         32 my %hash = @_;
223 10         17 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         42 );
230             }
231              
232             # No id given
233             else {
234 2         6 $entry = $self->add('entry');
235             };
236              
237             # Add information
238 10         78 foreach (keys %hash) {
239 9         26 $entry->add($_, $hash{$_});
240             };
241              
242 10         44 return $entry;
243             };
244              
245              
246             # Set or get generator information
247             sub generator {
248 4     4 1 2144 shift->_simple_feed_info(generator => @_);
249             };
250              
251              
252             # Set or get icon information
253             sub icon {
254 3     3 1 1429 shift->_simple_feed_info(icon => @_);
255             };
256              
257              
258             # Add id
259             sub id {
260 5     5 1 556 my $self = shift;
261              
262             # Get id
263 5 100       16 unless ($_[0]) {
264 2         5 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         7 my $id = shift;
270 3         17 my $element = $self->set(id => $id);
271 3 50       10 return unless $element;
272              
273             # Add xml:id also
274 3         19 $element->parent->attr('xml:id' => $id);
275 3         127 return $self;
276             };
277              
278              
279             # Add link information
280             sub link {
281 19     19 1 1886 my $self = shift;
282              
283 19 100       48 if ($_[1]) {
284              
285             # rel => href
286 11 100       43 if (@_ == 2) {
287 5         24 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     26 my $rel = delete $values{rel} || 'related';
297 6         37 return $self->add(link => {
298             rel => $rel,
299             %values
300             });
301             };
302              
303 8         11 my $rel = shift;
304              
305 8         10 my $children;
306             # Node is root
307 8 100       19 unless ($self->parent) {
308 5         57 $children = $self->at('*')->children('link');
309             }
310              
311             # Node is under root
312             else {
313 3         59 $children = $self->children('link');
314             };
315              
316 8     16   79 return $children->grep(sub { $_->attr('rel') eq $rel });
  16         198  
317             };
318              
319              
320             # Add logo
321             sub logo {
322 3     3 1 2377 shift->_simple_feed_info(logo => @_);
323             };
324              
325              
326             # Add publish time information
327             sub published {
328 14     14 1 1846 shift->_date(published => @_);
329             };
330              
331              
332             # Add rights information
333             sub rights {
334 3     3 1 1633 my $self = shift;
335              
336             # Set rights
337 3 100       16 return $self->_addset_text(set => rights => @_) if $_[0];
338              
339             # Return rights
340 1         5 return $self->_get_information_single('rights');
341             };
342              
343              
344             # Add source information to entry
345             sub source {
346 3     3 1 1801 my $self = shift;
347              
348             # Only valid in entry
349 3 50 33     9 return if !$self->tag || $self->tag ne 'entry';
350              
351             # Set source
352 3 100       85 return $self->set(source => @_) if $_[0];
353              
354             # Return source
355 2         5 return $self->_get_information_single('source');
356             };
357              
358              
359             # Add subtitle
360             sub subtitle {
361 10     10 1 3699 my $self = shift;
362              
363             # Only valid in feed or source or something
364 10 100 66     31 return if $self->tag && $self->tag eq 'entry';
365              
366             # Set subtitle
367 9 100       147 return $self->_addset_text(set => subtitle => @_) if $_[0];
368              
369             # Return subtitle
370 3         15 return $self->_get_information_single('subtitle');
371             };
372              
373              
374             # Add summary
375             sub summary {
376 12     12 1 2753 my $self = shift;
377              
378             # Only valid in entry
379 12 100 66     28 return if !$self->tag || $self->tag ne 'entry';
380              
381             # Set summary
382 11 100       285 return $self->_addset_text(set => summary => @_) if $_[0];
383              
384             # Return summary
385 5         14 return $self->_get_information_single('summary');
386             };
387              
388              
389             # Add title
390             sub title {
391 15     15 1 3211 my $self = shift;
392              
393             # Set title
394 15 100       50 return $self->_addset_text(set => title => @_) if $_[0];
395              
396             # Return title
397 6         17 return $self->_get_information_single('title');
398             };
399              
400              
401             # Add update time information
402             sub updated {
403 7     7 1 1000 shift->_date(updated => @_);
404             };
405              
406              
407             # Add person information
408             sub _add_person {
409 17     17   38 my $self = shift;
410 17         24 my $type = shift;
411              
412             # Person is a defined node
413 17 100       43 if (ref($_[0])) {
414 3         5 my $person = shift;
415 3         13 $person->root->at('*')->tree->[1] = $type;
416 3         72 return $self->add($person);
417             }
418              
419             # Person is a hash
420             else {
421 14         59 my $person = $self->add($type);
422 14         49 my %data = @_;
423              
424 14         45 foreach (keys %data) {
425 15 50       64 $person->add($_ => $data{$_} ) if $data{$_};
426             };
427 14         63 return $person;
428             };
429             };
430              
431              
432             # Add date construct
433             sub _date {
434 21     21   30 my $self = shift;
435 21         62 my $type = shift;
436              
437             # Set date
438 21 100       46 if ($_[0]) {
439 9         11 my $date = shift;
440              
441 9 50       21 unless (ref($date)) {
442 9         48 $date = XML::Loy::Date::RFC3339->new($date);
443             };
444              
445 9         32 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       67 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   105 my $self = shift;
462 73         115 my $action = shift;
463              
464 73 50 33     281 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         81 my $type = shift;
469              
470             # Text is a defined node
471 73 100       162 if (ref $_[0]) {
472              
473 37         48 my $text = shift;
474              
475             # Get root element
476 37         98 my $root_elem = $text->root->at('*');
477              
478 37         586 $root_elem->tree->[1] = $type;
479 37         246 my $root_att = $root_elem->attr;
480              
481             # Delete type
482 37   50     447 my $c_type = $root_att->{type} || '';
483 37 100       63 if ($c_type eq 'text') {
484 16         28 delete $root_elem->attr->{'type'};
485             };
486              
487 37         204 $text->root->at('*')->tree->[1] = $type;
488              
489 37         766 my $element = $self->$action($text);
490              
491             # Return wrapped div
492 37 100       428 return $element->at('div') if $c_type eq 'xhtml';
493              
494             # Return node
495 28         173 return $element;
496             };
497              
498 36         41 my $text;
499             # Text is no hash
500 36 100       80 unless (defined $_[1]) {
501 12         32 $text = $self->new_text(
502             type => 'text',
503             content => shift
504             );
505             }
506              
507             # Text is a hash
508             else {
509 24         55 $text = $self->new_text(@_);
510             };
511              
512             # Todo: Optimize!
513 36 100       136 return $self->_addset_text($action, $type, $text) if ref $text;
514 1         4 return;
515             };
516              
517              
518             # Return information of entries or the feed
519             sub _get_information_array {
520 17     17   25 my $self = shift;
521 17         32 my $type = shift;
522              
523             # Get author objects
524 17         51 my $children = $self->children($type);
525              
526             # Return information of object
527 17 50       122 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   46 my $self = shift;
537 34         45 my $type = shift;
538              
539             # Get author objects
540 34         85 my $children = $self->children($type);
541              
542             # Return information of object
543 34 50       258 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   18 my $self = shift;
554 10         48 my $type = shift;
555              
556 10         28 my $feed = $self->root->at('feed');
557 10 100       644 return unless $feed;
558              
559             # Set
560 7 100       42 if ($_[0]) {
561 6         17 return $feed->set($type => @_);
562             };
563              
564             # Get generator information
565 1         3 my $gen = $feed->at($type);
566 1 50       19 return $gen->all_text if $gen;
567 0           return;
568             };
569              
570              
571             1;
572              
573              
574             __END__