File Coverage

blib/lib/App/Rssfilter/Feed.pm
Criterion Covered Total %
statement 76 76 100.0
branch 17 24 70.8
condition 3 6 50.0
subroutine 14 14 100.0
pod n/a
total 110 120 91.6


line stmt bran cond sub pod time code
1 15     15   162358 use strict;
  15         26  
  15         388  
2 15     15   77 use warnings;
  15         25  
  15         675  
3              
4             # ABSTRACT: Get the latest or previous version of an RSS feed
5              
6              
7             package App::Rssfilter::Feed;
8             $App::Rssfilter::Feed::VERSION = '0.08'; # TRIAL
9 15     15   924 use Moo;
  15         15230  
  15         97  
10             with 'App::Rssfilter::Logger';
11 15     15   46666 use Method::Signatures;
  15         552815  
  15         115  
12              
13              
14              
15             has name => (
16             is => 'ro',
17             required => 1,
18             );
19              
20              
21             has url => (
22             is => 'ro',
23             required => 1,
24             );
25              
26              
27             has rules => (
28             is => 'ro',
29             default => sub { [] },
30             );
31              
32              
33             has user_agent => (
34             is => 'ro',
35 15     15   22359 default => sub { use Mojo::UserAgent; Mojo::UserAgent->new },
  15         3472552  
  15         215  
36             );
37              
38              
39             has storage => (
40 15     15   24596 is => 'lazy',
  15         55  
  15         1147  
41             default => method {
42             use App::Rssfilter::Feed::Storage;
43             App::Rssfilter::Feed::Storage->new(
44             name => $self->name,
45             );
46             },
47 15     15   36932 );
  14     14   1368314  
  14         106  
48 14 100       82  
49 4         16 method BUILDARGS( %options ) {
50 4         12 if( 1 == keys %options ) {
51             @options{ 'name', 'url' } = each %options;
52 14         342 delete $options{ $options{ name } };
53             }
54             return { %options };
55             }
56 15 50   15   48076  
  2     2   2283  
  2         10  
  2         4  
  2         7  
57 15     15   1496  
  15         31  
  15         1498  
58 2 100 66     19 method add_rule( $rule, @rule_options ) {
59 1         3 use Scalar::Util qw< blessed >;
60 15     15   6714 if ( ! blessed( $rule ) or ! $rule->isa( 'App::Rssfilter::Rule' ) ) {
  15         44  
  15         992  
61 1         9 unshift @rule_options, $rule; # restore original @_
62             use App::Rssfilter::Rule;
63             $rule = App::Rssfilter::Rule->new( @rule_options );
64 2         27 }
  2         8  
65 2         10  
66             push @{ $self->rules }, $rule;
67             return $self;
68             }
69 15 100 33 15   133845  
  8 50   8   2312  
  8 50       1952  
  8 50       50  
  8 50       134  
  8         43480  
  8         77  
  8         3900  
  8         51  
  8         38  
70 8         140  
71 8         635 method update( ArrayRef :$rules = [], :$storage = $self->storage ) {
72             $storage = $storage->set_name( $self->name );
73 8         408 my $old = $storage->load_existing;
74 8 100       88  
75 3         158 my $headers = {};
76 3         14463 if( defined( my $last_modified = $storage->last_modified ) ) {
  3         12  
77             $self->logger->debug( "last update was $last_modified" );
78             ${ $headers }{ 'If-Modified-Since' } = $last_modified;
79 8         357 }
80              
81             my $latest = $self->user_agent->get(
82             $self->url,
83             $headers
84 8         7894 );
  8         29  
85 8         22  
  8         45  
86             my @rules = @{ $rules };
87 8 100       38 push @rules, @{ $self->rules };
88 5         80  
89 5         11646 if ( 200 == $latest->res->code ) {
90 5         237 $self->logger->debug( 'found a newer feed!' );
91 5         3749 $self->logger->debug( 'filtering '. $self->name );
92 6 50       137 my $new = $latest->res->dom;
93 6         341 for my $rule ( @rules ) {
94             $self->logger->debugf( 'applying %s => %s to new feed', $rule->condition_name, $rule->action_name ) if $self->logger->is_debug;
95 5         235 $rule->constrain( $new );
96             }
97             $storage->save_feed( $new );
98 8 50       2192 }
99 8         127  
100 8         16716 if ( defined $old ) {
101 8         155 $self->logger->debug( 'collecting guids from old feed' );
102             for my $rule ( @rules ) {
103             $rule->constrain( $old );
104             }
105             }
106             }
107              
108             1;
109              
110             __END__
111              
112             =pod
113              
114             =encoding UTF-8
115              
116             =head1 NAME
117              
118             App::Rssfilter::Feed - Get the latest or previous version of an RSS feed
119              
120             =head1 VERSION
121              
122             version 0.08
123              
124             =head1 SYNOPSIS
125              
126             use App::Rssfilter::Feed;
127              
128             my $feed = App::Rssfilter::Feed->new( filename => 'http://get.your.files/here.rss' );
129             # shorthand for
130             $feed = App::Rssfilter::Feed->new(
131             name => 'filename',
132             url => 'http://get.your.files/here.rss',
133             );
134              
135             my $rule = App::RssFilter::Rule->new(
136             condition => 'A Matcher',
137             action => 'A Filter',
138             );
139             $feed->add_rule( $rule );
140              
141             $feed->add_rule(
142             condition => 'Another Matcher',
143             action => 'Another Filter',
144             );
145              
146             $feed->update;
147              
148             ### or with App::Rssfilter::Group
149              
150             use App::Rssfilter::Group;
151             my $group = App::RssFilter::Group->new( 'Tubular' );
152             $group->add_feed( RadName => 'http://r.a.d.i.c.al/feed.rss' );
153             # shorthand for
154             $group->add_feed(
155             App::Rssfilter::Feed->new(
156             RadName => 'http://r.a.d.i.c.al/feed.rss'
157             )
158             );
159             $group->update;
160              
161             =head1 DESCRIPTION
162              
163             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>.
164              
165             It consumes the L<App::Rssfilter::Logger> role.
166              
167             =head1 ATTRIBUTES
168              
169             =head2 logger
170              
171             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.
172              
173             =head2 name
174              
175             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.
176              
177             =head2 url
178              
179             This is the URL to fetch the latest feed content from, and is required.
180              
181             =head2 rules
182              
183             This is the arrayref of L<rules|App::Rssfilter::Rule> which will constrain newly-fetched feeds. It defaults to an empty arrayref.
184              
185             =head2 user_agent
186              
187             This is a L<Mojo::UserAgent> to use to fetch this feed's C<url>. It defaults to a new L<Mojo::UserAgent>.
188              
189             =head2 storage
190              
191             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.
192              
193             =head1 METHODS
194              
195             =head2 add_rule
196              
197             $feed->add_rule( $rule )->add_rule( %rule_parameters );
198              
199             Adds the C<$rule> (or creates a new L<App::RssFilter::Rule> from the passed parameters) to the rules.
200              
201             =head2 update
202              
203             $feed->update( rules => $rules, storage => $storage );
204              
205             This method will:
206              
207             =over 4
208              
209             =item *
210              
211             download the RSS feed from the URL, if it is newer than the previously-saved version
212              
213             =item *
214              
215             apply the rules to the new RSS feed
216              
217             =item *
218              
219             save the new RSS feed
220              
221             =item *
222              
223             apply the rules to the old RSS feed
224              
225             =back
226              
227             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.
228              
229             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.
230              
231             =head1 SEE ALSO
232              
233             =over 4
234              
235             =item *
236              
237             L<App::RssFilter::Feed::Storage>
238              
239             =item *
240              
241             L<App::RssFilter::Group>
242              
243             =item *
244              
245             L<App::RssFilter::Rule>
246              
247             =item *
248              
249             L<App::RssFilter>
250              
251             =back
252              
253             =head1 AUTHOR
254              
255             Daniel Holz <dgholz@gmail.com>
256              
257             =head1 COPYRIGHT AND LICENSE
258              
259             This software is copyright (c) 2015 by Daniel Holz.
260              
261             This is free software; you can redistribute it and/or modify it under
262             the same terms as the Perl 5 programming language system itself.
263              
264             =cut