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   78597 use Carp qw/carp/;
  9         27  
  9         530  
3 9     9   52 use strict;
  9         14  
  9         188  
4 9     9   41 use warnings;
  9         14  
  9         257  
5 9     9   3210 use Mojo::ByteStream 'b';
  9         1215478  
  9         503  
6 9     9   5067 use XML::Loy::Date::RFC3339;
  9         25  
  9         600  
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         62 use XML::Loy with => (
15             mime => 'application/atom+xml',
16             prefix => 'atom',
17             namespace => 'http://www.w3.org/2005/Atom'
18 9     9   4355 );
  9         32  
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 2011 my $self = shift;
28 3         20 my $person = ref($self)->SUPER::new('person');
29              
30 3         12 my %hash = @_;
31 3         19 $person->set($_ => $hash{$_}) foreach keys %hash;
32 3         13 return $person;
33             };
34              
35              
36             # New text construct
37             sub new_text {
38 47     47 1 5674 my $self = shift;
39              
40 47 50       95 return unless $_[0];
41              
42 47         70 my $class = ref($self);
43              
44             # Expect empty html
45 47 100       90 unless (defined $_[1]) {
46 5         31 return $class->SUPER::new(
47             text => {
48             type => 'text',
49             -type => 'raw'
50             } => shift );
51             };
52              
53 42         57 my ($type, $content, %hash);
54              
55             # Only textual content
56 42 100 100     264 if (!defined $_[2] && $_[0] =~ m/(?:text|x?html)/) {
    50          
57 21         48 $type = shift;
58 21         27 $content = shift;
59             }
60              
61             # Hash definition
62             elsif ((@_ % 2) == 0) {
63 21         79 %hash = @_;
64              
65 21   100     70 $type = delete $hash{type} || 'text';
66              
67 21 50       49 if (exists $hash{src}) {
68 0         0 return $class->SUPER::new(
69             text => { type => $type, %hash }
70             );
71             };
72              
73 21 100       58 $content = delete $hash{content} or return;
74             };
75              
76             # Content node
77 41         70 my $c_node;
78              
79             # xhtml
80 41 100 100     192 if ($type eq 'xhtml') {
    100          
    50          
81              
82             # Create new by hash
83 11         65 $c_node = $class->SUPER::new(
84             text => {
85             type => $type,
86             %hash
87             });
88              
89             # XHTML content - allowed to be pretty printed
90 11         61 $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         180 $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         29 $c_node = $class->new(
123             text => {
124             type => $type,
125             -type => 'armour',
126             %hash
127             },
128             $content);
129             };
130              
131 41         2309 return $c_node;
132             };
133              
134              
135             # Add author information
136             sub author {
137 25     25 1 2555 my $self = shift;
138              
139             # Add author
140 25 100       97 return $self->_add_person(author => @_) if $_[0];
141              
142             # Get author information
143 11         34 return $self->_get_information_array('author');
144             };
145              
146              
147             # Add category information
148             sub category {
149 5     5 1 927 my $self = shift;
150              
151             # Set category
152 5 100       14 if ($_[0]) {
153 2 100       6 if (!defined $_[1]) {
154 1         5 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   24 $coll->map(sub { $_ = $_->{term} });
  6         82  
166             };
167              
168 3         73 return $coll;
169             };
170              
171              
172             # Add contributor information
173             sub contributor {
174 6     6 1 1908 my $self = shift;
175              
176             # Add contributor
177 6 100       21 return $self->_add_person(contributor => @_) if $_[0];
178              
179             # Get contributor information
180 3         8 return $self->_get_information_array('contributor');
181             };
182              
183              
184             # Add content information
185             sub content {
186 18     18 1 4729 my $self = shift;
187              
188             # Set content
189 18 100       62 return $self->_addset_text(set => content => @_) if $_[0];
190              
191             # Return content
192 3         8 return $self->_get_information_single('content');
193             };
194              
195              
196             # Set or get entry
197             sub entry {
198 15     15 1 5613 my $self = shift;
199              
200             # Is object
201 15 50 100     112 if (ref $_[0]) {
    100          
202 0         0 return $self->add(@_);
203             }
204              
205             # Get entry
206             elsif ($_[0] && !$_[1]) {
207              
208 5         9 my $id = shift;
209              
210             # Get based on xml:id
211 5         20 my $entry = $self->at(qq{entry[xml\\:id="$id"]});
212 5 100       987 return $entry if $entry;
213              
214             # Get based on id
215 2     6   6 my $idc = $self->find('entry > id')->grep(sub { $_->text eq $id });
  6         188  
216              
217 2 50 33     71 return unless $idc && $idc->[0];
218              
219 2         60 return $idc->[0]->parent;
220             };
221              
222 10         41 my %hash = @_;
223 10         17 my $entry;
224              
225             # Set id additionally as xml:id
226 10 100       27 if (exists $hash{id}) {
227             $entry = $self->add(
228             entry => {'xml:id' => $hash{id}}
229 8         52 );
230             }
231              
232             # No id given
233             else {
234 2         9 $entry = $self->add('entry');
235             };
236              
237             # Add information
238 10         40 foreach (keys %hash) {
239 9         31 $entry->add($_, $hash{$_});
240             };
241              
242 10         56 return $entry;
243             };
244              
245              
246             # Set or get generator information
247             sub generator {
248 4     4 1 1849 shift->_simple_feed_info(generator => @_);
249             };
250              
251              
252             # Set or get icon information
253             sub icon {
254 3     3 1 1232 shift->_simple_feed_info(icon => @_);
255             };
256              
257              
258             # Add id
259             sub id {
260 5     5 1 600 my $self = shift;
261              
262             # Get id
263 5 100       15 unless ($_[0]) {
264 2         7 my $id_obj = $self->_get_information_single('id');
265 2 50       13 return $id_obj->text if $id_obj;
266 0         0 return;
267             };
268              
269 3         5 my $id = shift;
270 3         16 my $element = $self->set(id => $id);
271 3 50       8 return unless $element;
272              
273             # Add xml:id also
274 3         22 $element->parent->attr('xml:id' => $id);
275 3         131 return $self;
276             };
277              
278              
279             # Add link information
280             sub link {
281 19     19 1 1811 my $self = shift;
282              
283 19 100       59 if ($_[1]) {
284              
285             # rel => href
286 11 100       29 if (@_ == 2) {
287 5         36 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     30 my $rel = delete $values{rel} || 'related';
297 6         51 return $self->add(link => {
298             rel => $rel,
299             %values
300             });
301             };
302              
303 8         14 my $rel = shift;
304              
305 8         9 my $children;
306             # Node is root
307 8 100       24 unless ($self->parent) {
308 5         88 $children = $self->at('*')->children('link');
309             }
310              
311             # Node is under root
312             else {
313 3         107 $children = $self->children('link');
314             };
315              
316 8     16   92 return $children->grep(sub { $_->attr('rel') eq $rel });
  16         246  
317             };
318              
319              
320             # Add logo
321             sub logo {
322 3     3 1 1827 shift->_simple_feed_info(logo => @_);
323             };
324              
325              
326             # Add publish time information
327             sub published {
328 14     14 1 1914 shift->_date(published => @_);
329             };
330              
331              
332             # Add rights information
333             sub rights {
334 3     3 1 1245 my $self = shift;
335              
336             # Set rights
337 3 100       12 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 1877 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       100 return $self->set(source => @_) if $_[0];
353              
354             # Return source
355 2         6 return $self->_get_information_single('source');
356             };
357              
358              
359             # Add subtitle
360             sub subtitle {
361 10     10 1 2826 my $self = shift;
362              
363             # Only valid in feed or source or something
364 10 100 66     32 return if $self->tag && $self->tag eq 'entry';
365              
366             # Set subtitle
367 9 100       156 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 3009 my $self = shift;
377              
378             # Only valid in entry
379 12 100 66     41 return if !$self->tag || $self->tag ne 'entry';
380              
381             # Set summary
382 11 100       354 return $self->_addset_text(set => summary => @_) if $_[0];
383              
384             # Return summary
385 5         17 return $self->_get_information_single('summary');
386             };
387              
388              
389             # Add title
390             sub title {
391 15     15 1 3499 my $self = shift;
392              
393             # Set title
394 15 100       101 return $self->_addset_text(set => title => @_) if $_[0];
395              
396             # Return title
397 6         20 return $self->_get_information_single('title');
398             };
399              
400              
401             # Add update time information
402             sub updated {
403 7     7 1 1033 shift->_date(updated => @_);
404             };
405              
406              
407             # Add person information
408             sub _add_person {
409 17     17   42 my $self = shift;
410 17         26 my $type = shift;
411              
412             # Person is a defined node
413 17 100       67 if (ref($_[0])) {
414 3         6 my $person = shift;
415 3         12 $person->root->at('*')->tree->[1] = $type;
416 3         73 return $self->add($person);
417             }
418              
419             # Person is a hash
420             else {
421 14         73 my $person = $self->add($type);
422 14         73 my %data = @_;
423              
424 14         53 foreach (keys %data) {
425 15 50       100 $person->add($_ => $data{$_} ) if $data{$_};
426             };
427 14         72 return $person;
428             };
429             };
430              
431              
432             # Add date construct
433             sub _date {
434 21     21   37 my $self = shift;
435 21         62 my $type = shift;
436              
437             # Set date
438 21 100       52 if ($_[0]) {
439 9         15 my $date = shift;
440              
441 9 50       22 unless (ref($date)) {
442 9         55 $date = XML::Loy::Date::RFC3339->new($date);
443             };
444              
445 9         29 return $self->set($type, $date->to_string);
446             };
447              
448             # Get published information
449 12         33 my $date = $self->_get_information_single($type);
450              
451             # Parse date
452 12 50       84 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   101 my $self = shift;
462 73         117 my $action = shift;
463              
464 73 50 33     298 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         99 my $type = shift;
469              
470             # Text is a defined node
471 73 100       140 if (ref $_[0]) {
472              
473 37         40 my $text = shift;
474              
475             # Get root element
476 37         98 my $root_elem = $text->root->at('*');
477              
478 37         664 $root_elem->tree->[1] = $type;
479 37         287 my $root_att = $root_elem->attr;
480              
481             # Delete type
482 37   50     478 my $c_type = $root_att->{type} || '';
483 37 100       77 if ($c_type eq 'text') {
484 16         25 delete $root_elem->attr->{'type'};
485             };
486              
487 37         244 $text->root->at('*')->tree->[1] = $type;
488              
489 37         885 my $element = $self->$action($text);
490              
491             # Return wrapped div
492 37 100       442 return $element->at('div') if $c_type eq 'xhtml';
493              
494             # Return node
495 28         161 return $element;
496             };
497              
498 36         41 my $text;
499             # Text is no hash
500 36 100       68 unless (defined $_[1]) {
501 12         36 $text = $self->new_text(
502             type => 'text',
503             content => shift
504             );
505             }
506              
507             # Text is a hash
508             else {
509 24         61 $text = $self->new_text(@_);
510             };
511              
512             # Todo: Optimize!
513 36 100       133 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   25 my $self = shift;
521 17         28 my $type = shift;
522              
523             # Get author objects
524 17         52 my $children = $self->children($type);
525              
526             # Return information of object
527 17 50       137 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         48 my $type = shift;
538              
539             # Get author objects
540 34         102 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   16 my $self = shift;
554 10         16 my $type = shift;
555              
556 10         28 my $feed = $self->root->at('feed');
557 10 100       674 return unless $feed;
558              
559             # Set
560 7 100       40 if ($_[0]) {
561 6         16 return $feed->set($type => @_);
562             };
563              
564             # Get generator information
565 1         2 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__