File Coverage

blib/lib/App/Rssfilter/Feed.pm
Criterion Covered Total %
statement 76 76 100.0
branch 17 24 70.8
condition 5 9 55.5
subroutine 14 14 100.0
pod n/a
total 112 123 91.0


line stmt bran cond sub pod time code
1 15     15   218249 use strict;
  15         36  
  15         721  
2 15     15   89 use warnings;
  15         33  
  15         982  
3              
4             # ABSTRACT: Get the latest or previous version of an RSS feed
5              
6              
7             package App::Rssfilter::Feed;
8             {
9             $App::Rssfilter::Feed::VERSION = '0.07';
10             }
11              
12 15     15   1314 use Moo;
  15         26189  
  15         134  
13             with 'App::Rssfilter::Logger';
14 15     15   53992 use Method::Signatures;
  15         592204  
  15         157  
15              
16              
17              
18             has name => (
19             is => 'ro',
20             required => 1,
21             );
22              
23              
24             has url => (
25             is => 'ro',
26             required => 1,
27             );
28              
29              
30             has rules => (
31             is => 'ro',
32             default => sub { [] },
33             );
34              
35              
36             has user_agent => (
37             is => 'ro',
38 15     15   30261 default => sub { use Mojo::UserAgent; Mojo::UserAgent->new },
  15         7949879  
  15         243  
39             );
40              
41              
42             has storage => (
43 15     15   23690 is => 'lazy',
  15         54  
  15         1425  
44             default => method {
45             use App::Rssfilter::Feed::Storage;
46             App::Rssfilter::Feed::Storage->new(
47             name => $self->name,
48             );
49             },
50 15     15   37829 );
  14     14   2255233  
  14         130  
51 14 100       93  
52 4         18 method BUILDARGS( %options ) {
53 4         25 if( 1 == keys %options ) {
54             @options{ 'name', 'url' } = each %options;
55 14         608 delete $options{ $options{ name } };
56             }
57             return { %options };
58             }
59 15 50   15   48037  
  2     2   3788  
  2         10  
  2         5  
  2         9  
60 15     15   1989  
  15         36  
  15         1770  
61 2 100 66     24 method add_rule( $rule, @rule_options ) {
62 1         4 use Scalar::Util qw< blessed >;
63 15     15   8536 if ( ! blessed( $rule ) or ! $rule->isa( 'App::Rssfilter::Rule' ) ) {
  15         2439  
  15         1312  
64 1         12 unshift @rule_options, $rule; # restore original @_
65             use App::Rssfilter::Rule;
66             $rule = App::Rssfilter::Rule->new( @rule_options );
67 2         165 }
  2         13  
68 2         13  
69             push @{ $self->rules }, $rule;
70             return $self;
71             }
72 15 50 33 15   186238  
  8 100 66 8   4275  
  8 50       37  
  8 50       271  
  8 50       1035030  
  8         29  
  8         68  
  8         4328  
  8         52  
  8         41  
73 8         145  
74 8         713 method update( ArrayRef :$rules = [], :$storage = $self->storage ) {
75             $storage = $storage->set_name( $self->name );
76 8         474 my $old = $storage->load_existing;
77 8 100       83  
78 3         167 my $headers = {};
79 3         194 if( defined( my $last_modified = $storage->last_modified ) ) {
  3         12  
80             $self->logger->debug( "last update was $last_modified" );
81             ${ $headers }{ 'If-Modified-Since' } = $last_modified;
82 8         383 }
83              
84             my $latest = $self->user_agent->get(
85             $self->url,
86             $headers
87 8         15848 );
  8         181  
88 8         21  
  8         69  
89             my @rules = @{ $rules };
90 8 100       344 push @rules, @{ $self->rules };
91 5         357  
92 5         214 if ( 200 == $latest->res->code ) {
93 5         259 $self->logger->debug( 'found a newer feed!' );
94 5         3475 $self->logger->debug( 'filtering '. $self->name );
95 6 50       170 my $new = $latest->res->dom;
96 6         308 for my $rule ( @rules ) {
97             $self->logger->debugf( 'applying %s => %s to new feed', $rule->condition_name, $rule->action_name ) if $self->logger->is_debug;
98 5         830 $rule->constrain( $new );
99             }
100             $storage->save_feed( $new );
101 8 50       609 }
102 8         49  
103 8         399 if ( defined $old ) {
104 8         151 $self->logger->debug( 'collecting guids from old feed' );
105             for my $rule ( @rules ) {
106             $rule->constrain( $old );
107             }
108             }
109             }
110              
111             1;
112              
113             __END__
114              
115             =pod
116              
117             =encoding UTF-8
118              
119             =head1 NAME
120              
121             App::Rssfilter::Feed - Get the latest or previous version of an RSS feed
122              
123             =head1 VERSION
124              
125             version 0.07
126              
127             =head1 SYNOPSIS
128              
129             use App::Rssfilter::Feed;
130              
131             my $feed = App::Rssfilter::Feed->new( filename => 'http://get.your.files/here.rss' );
132             # shorthand for
133             $feed = App::Rssfilter::Feed->new(
134             name => 'filename',
135             url => 'http://get.your.files/here.rss',
136             );
137              
138             my $rule = App::RssFilter::Rule->new(
139             condition => 'A Matcher',
140             action => 'A Filter',
141             );
142             $feed->add_rule( $rule );
143              
144             $feed->add_rule(
145             condition => 'Another Matcher',
146             action => 'Another Filter',
147             );
148              
149             $feed->update;
150              
151             ### or with App::Rssfilter::Group
152              
153             use App::Rssfilter::Group;
154             my $group = App::RssFilter::Group->new( 'Tubular' );
155             $group->add_feed( RadName => 'http://r.a.d.i.c.al/feed.rss' );
156             # shorthand for
157             $group->add_feed(
158             App::Rssfilter::Feed->new(
159             RadName => 'http://r.a.d.i.c.al/feed.rss'
160             )
161             );
162             $group->update;
163              
164             =head1 DESCRIPTION
165              
166             This module fetches the latest version of an RSS feed from a URL and constrains it with its list of L<rules|App::Rssfilter::Rule>.
167              
168             It consumes the L<App::Rssfilter::Logger> role.
169              
170             =head1 ATTRIBUTES
171              
172             =head2 logger
173              
174             This is a object used for logging. It defaults to a L<Log::Any> object. It is provided by the L<App::Rssfilter::Logger> role.
175              
176             =head2 name
177              
178             This is the name of the feed to use when storing it, and is required. This will be used by the default C<storage> as the filename to store the feed under.
179              
180             =head2 url
181              
182             This is the URL to fetch the latest feed content from, and is required.
183              
184             =head2 rules
185              
186             This is the arrayref of L<rules|App::Rssfilter::Rule> which will constrain newly-fetched feeds. It defaults to an empty arrayref.
187              
188             =head2 user_agent
189              
190             This is a L<Mojo::UserAgent> to use to fetch this feed's C<url>. It defaults to a new L<Mojo::UserAgent>.
191              
192             =head2 storage
193              
194             This is the L<App::Rssfilter::Feed::Storage> to store newly-fetched iRSS documents, or retrieve the previously-fetched version. It defaults to a new L<App::Rssfilter::Feed::Storage>, with its name set to this feed's name.
195              
196             =head1 METHODS
197              
198             =head2 add_rule
199              
200             $feed->add_rule( $rule )->add_rule( %rule_parameters );
201              
202             Adds the C<$rule> (or creates a new L<App::RssFilter::Rule> from the passed parameters) to the rules.
203              
204             =head2 update
205              
206             $feed->update( rules => $rules, storage => $storage );
207              
208             This method will:
209              
210             =over 4
211              
212             =item *
213              
214             download the RSS feed from the URL, if it is newer than the previously-saved version
215              
216             =item *
217              
218             apply the rules to the new RSS feed
219              
220             =item *
221              
222             save the new RSS feed
223              
224             =item *
225              
226             apply the rules to the old RSS feed
227              
228             =back
229              
230             The old feed has rules applied to it so that any group-wide rules will always see all of the latest items, even if a feed does not have a newer version available.
231              
232             The parameters are optional. C<$rules> should be an arryref of additional rules to be added to the feed's C<rules> for this update only. C<$storage> should be an L<App::Rssfilter::Feed::Storage> that will used instead of this feed's C<storage> to load/save RSS doucments.
233              
234             =head1 SEE ALSO
235              
236             =over 4
237              
238             =item *
239              
240             L<App::RssFilter::Feed::Storage>
241              
242             =item *
243              
244             L<App::RssFilter::Group>
245              
246             =item *
247              
248             L<App::RssFilter::Rule>
249              
250             =item *
251              
252             L<App::RssFilter>
253              
254             =back
255              
256             =head1 AUTHOR
257              
258             Daniel Holz <dgholz@gmail.com>
259              
260             =head1 COPYRIGHT AND LICENSE
261              
262             This software is copyright (c) 2013 by Daniel Holz.
263              
264             This is free software; you can redistribute it and/or modify it under
265             the same terms as the Perl 5 programming language system itself.
266              
267             =cut