File Coverage

blib/lib/Mojo/Feed/Reader.pm
Criterion Covered Total %
statement 68 71 95.7
branch 36 44 81.8
condition 9 14 64.2
subroutine 14 15 93.3
pod 3 3 100.0
total 130 147 88.4


line stmt bran cond sub pod time code
1             package Mojo::Feed::Reader;
2 8     8   1797961 use Mojo::Base -base;
  8         60  
  8         51  
3              
4 8     8   3043 use Mojo::UserAgent;
  8         785418  
  8         55  
5 8     8   3181 use Mojo::Feed;
  8         21  
  8         47  
6 8     8   304 use Mojo::File 'path';
  8         27  
  8         471  
7 8     8   52 use Mojo::Util 'decode', 'trim';
  8         17  
  8         412  
8 8     8   49 use Carp qw(carp croak);
  8         15  
  8         433  
9 8     8   46 use Scalar::Util qw(blessed);
  8         21  
  8         10215  
10              
11             has charset => 'UTF-8';
12              
13             has ua => sub { Mojo::UserAgent->new };
14              
15             sub parse {
16 48     48 1 258976 my ( $self, $xml, $charset ) = @_;
17 48 50       205 return undef unless ($xml);
18 48         242 my ( $body, $source, $url, $file, $feed );
19 48 100       185 if ( $body = $self->_from_string($xml) ) {
    100          
    50          
20 8         44 $feed = Mojo::Feed->new( body => $body );
21             }
22             elsif ( $file = $self->_from_file($xml) ) {
23 32         287 $feed = Mojo::Feed->new( file => $file );
24             }
25             elsif ( $url = $self->_from_url($xml) ) {
26 8         107 $feed = Mojo::Feed->new( url => $url, ua => $self->ua );
27             }
28             else {
29 0         0 croak "unknown argument $xml";
30             }
31 48 50       512 $feed->charset($charset) if ($charset);
32 48 100       181 return ( $feed->is_valid ) ? $feed : undef;
33             }
34              
35             sub _from_string {
36 48     48   122 my ( $self, $xml ) = @_;
37 48 100       214 my $str = ( !ref $xml ) ? $xml : ( ref $xml eq 'SCALAR' ) ? $$xml : '';
    100          
38 48 100       333 return ( $str =~ /^\s*\
39             }
40              
41             sub _from_url {
42 8     8   30 my ( $self, $xml ) = @_;
43 8 0 33     103 my $url =
    50          
44             ( blessed $xml && $xml->isa('Mojo::URL') ) ? $xml->clone()
45             : ( $xml =~ /^https?\:/ ) ? Mojo::URL->new("$xml")
46             : undef;
47 8         463 return $url;
48             }
49              
50             sub _from_file {
51 40     40   111 my ( $self, $xml ) = @_;
52 40 100 66     886 my $file =
    50          
    100          
53             ( ref $xml )
54             ? ( blessed $xml && $xml->can('slurp') )
55             ? $xml
56             : undef
57             : ( -r "$xml" ) ? Mojo::File->new($xml)
58             : undef;
59 40         470 return $file;
60             }
61              
62             # discover - get RSS/Atom feed URL from argument.
63             # Code adapted to use Mojolicious from Feed::Find by Benjamin Trott
64             # Any stupid mistakes are my own
65             sub discover {
66 27     27 1 87789 my ( $self, $url ) = @_;
67              
68             # $self->ua->max_redirects(5)->connect_timeout(30);
69             return $self->ua->get_p($url)
70 0     0   0 ->catch( sub { my ($err) = shift; croak "Connection Error: $err" } )
  0         0  
71             ->then(
72             sub {
73 27     27   189971 my ($tx) = @_;
74 27 100 66     103 if ( $tx->res->is_success && $tx->res->code == 200 ) {
75 25         787 my $feed = Mojo::Feed->new(url => $tx->req->url);
76 25 100       397 return $feed->url if ($feed->is_feed_content_type($tx->res->headers->content_type));
77 21         96 my @feeds = $feed->find_feed_links($tx->res);
78 21 100       126 return @feeds if (@feeds);
79 10         64 $feed->body($tx->res->body);
80 10 100       383 $feed->charset($tx->res->content->charset) if ($tx->res->content->charset);
81 10 100       537 return $feed->url if ($feed->is_valid);
82             }
83 10         471499 return;
84             }
85 27         115 );
86             }
87              
88             sub parse_opml {
89 6     6 1 21599 my ( $self, $opml_file ) = @_;
90 6 100       31 my $opml_str = decode $self->charset,
91             ( ref $opml_file )
92             ? $opml_file->slurp
93             : Mojo::File->new($opml_file)->slurp;
94 6         3553 my $d = Mojo::DOM->new->parse($opml_str);
95 6         357792 my ( %subscriptions, %categories );
96 6         50 for my $item ( $d->find(q{outline})->each ) {
97 1924         456387 my $node = $item->attr;
98 1924 100       22870 if ( !defined $node->{xmlUrl} ) {
99 80   66     379 my $cat = $node->{title} || $node->{text};
100 80         207 $categories{$cat} =
101             $item->children('[xmlUrl]')->map( 'attr', 'xmlUrl' );
102             }
103             else { # file by RSS URL:
104 1844         6176 $subscriptions{ $node->{xmlUrl} } = $node;
105             }
106             }
107              
108             # assign categories
109 6         725 for my $cat ( keys %categories ) {
110 80         227 for my $rss ( $categories{$cat}->each ) {
111             next
112 1840 50       4718 unless ( $subscriptions{$rss} )
113             ; # don't auto-vivify for empty "categories"
114 1840   100     6575 $subscriptions{$rss}{'categories'} ||= [];
115 1840         2204 push @{ $subscriptions{$rss}{'categories'} }, $cat;
  1840         4297  
116             }
117             }
118 6         3678 return ( values %subscriptions );
119             }
120              
121             1;
122             __END__