File Coverage

blib/lib/WWW/Comix/Plugin/GoComics.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package WWW::Comix::Plugin::GoComics;
2 1     1   2403 use strict;
  1         3  
  1         37  
3 1     1   15 use warnings;
  1         1  
  1         26  
4 1     1   5 use Carp;
  1         1  
  1         60  
5 1     1   494 use Moose::Policy 'Moose::Policy::FollowPBP';
  0            
  0            
6             use Moose;
7             use Readonly;
8             use HTML::Entities qw( decode_entities );
9             use HTML::LinkExtor;
10              
11             Readonly my $HOMEPAGE => 'http://www.gocomics.com';
12             Readonly my $PROBEPAGE => 'http://www.gocomics.com/features/';
13             Readonly my $STRIPSURI => 'http://picayune.uclick.com/comics';
14              
15             extends qw( WWW::Comix::Plugin );
16              
17             has last_visited => (is => 'rw');
18              
19             sub get_name { return 'Go Comics'; }
20             sub get_priority { return -5 }
21              
22             sub probe {
23             my $sp = shift;
24             my $agent = $sp->get_agent(); # automatically DWIM
25             my $res = $agent->get($PROBEPAGE);
26             croak "couldn't get probe page '$PROBEPAGE': ", $res->status_line()
27             unless $res->is_success();
28              
29             # Encoded with gzip by default! Use decoded_content
30             my ($section) = $res->decoded_content() =~ m{
31             >Comic \s+ Strips:</h3>
32             (.*?)
33             <br
34             }mxs;
35              
36             croak "no section found!" unless defined $section;
37              
38             my %config_for;
39             for my $line (split /\n/, $section) {
40             my ($url, $name) =
41             $line =~ m{<a \s+ href="(.+?)" .*? > (.+?) </a>}mxs
42             or next;
43             $name = decode_entities($name);
44             $name =~ s{<.*?>}{ }gmxs;
45             $config_for{decode_entities($name)} = $HOMEPAGE . $url;
46             } ## end for my $line (split /\n/...
47             $sp->set_config(%config_for);
48              
49             return;
50             } ## end sub probe
51              
52             sub _get_calendar_iterator {
53             my ($self, $year, $month, $featureCode, $featureID) = @_;
54             my $agent = $self->get_agent();
55             my $calendar_uri = "$HOMEPAGE/features/$featureID/calendar.js";
56             return sub {
57             return unless $year;
58              
59             my $uri = "$calendar_uri?year=$year&month=$month";
60             my $res = $agent->get($uri);
61             if (! $res->is_success()) {
62             carp "couldn't get calendar at '$uri'";
63             return;
64             }
65             my $json = $res->decoded_content();
66              
67             # Build up response
68             my @uris;
69             while (
70             $json =~ m{
71             "page_url" \s*:\s*
72             "
73             [^"]+ \\/ (\d{4}) \\/ (\d\d) \\/ (\d\d)
74             "
75             }gmxs
76             )
77             {
78             my ($y, $m, $d) = ($1, $2, $3);
79             my $sy = substr $y, 2; # last two digits in year
80             push @uris, "$STRIPSURI/$featureCode/$y/$featureCode$sy$m$d.gif";
81             } ## end while ($json =~ m{ )
82             @uris = reverse sort @uris;
83              
84             # Prepare for next iteration
85             ($year, $month) = (); # exhausted by default
86             if (@uris
87             && (my ($previous) = $json =~ m/"previous"\s*:\s*{(.*?)}/mxs))
88             {
89             ($year) = $previous =~ m/"year"\s*:\s*(\d+)/mxs;
90             ($month) = $previous =~ m/"month"\s*:\s*(\d+)/mxs;
91             } ## end if (@uris && (my ($previous...
92              
93             return @uris if wantarray;
94             return \@uris;
95             }
96             } ## end sub _get_calendar_iterator
97              
98             sub get_id_iterator {
99             my $self = shift;
100             my $agent = $self->get_agent();
101              
102             # Get homepage for comic, where data to get calendar lie in javascript
103             my $config_for = $self->get_config();
104             my $comic = $self->get_comic();
105             my $cpage = $config_for->{$comic} or croak "unhandled comic '$comic'";
106             my $res = $agent->get($cpage);
107             croak "couldn't get '$comic' page '$cpage': ", $res->status_line()
108             unless $res->is_success();
109              
110             my ($year, $month, $featureCode, $featureID) =
111             $res->decoded_content() =~ m{
112             year \s* : \s* (\d+) \s*, \s*
113             month \s* : \s* (\d+) \s*, \s*
114             .*?
115             featureCode \s* : \s* '(.*?)' \s*, \s*
116             featureID \s* : \s* (\d+),
117             }mxs or croak "could not find calendar data for $comic in '$cpage'";
118              
119             my $calendar_iterator =
120             $self->_get_calendar_iterator($year, $month, $featureCode,
121             $featureID);
122              
123             my @uris;
124             return sub {
125             @uris = $calendar_iterator->() unless @uris;
126             return shift @uris;
127             };
128              
129             } ## end sub get_id_iterator
130              
131             sub get_available_ids {
132             my $iterator = shift->get_id_iterator();
133             local $_;
134             my @retval;
135             push @retval, $_ while $_ = $iterator->();
136             return @retval;
137             } ## end sub get_available_ids
138              
139             sub id_to_uri {
140             my ($self, $id) = @_;
141             return $id;
142             } ## end sub id_to_uri
143              
144             1; # Magic true value required at end of module
145             __END__
146              
147             =head1 NAME
148              
149             WWW::Comix::Plugin::GoComics - WWW::Comix plugin for http://www.gocomics.com/
150              
151             =head1 DESCRIPTION
152              
153             This module is not inteded for direct usage, see
154             L<WWW::Comix> and L<WWW::Comix::Plugin>.
155              
156             B<Note>: the L<WWW::Comix::Plugin/get_available_ids> method in this plugin
157             is particularly heavy (network-wise speaking). You should avoid using it,
158             and favour the iterator approach.
159              
160             =head1 DIAGNOSTICS
161              
162             For each of the following messages, chances are that Go Comics changed
163             something in their web site.
164              
165             =over
166              
167             =item C<< no section found! >>
168              
169             =item C<< couldn't get '%s' page '%s': %s >>
170              
171             =item C<< could not find calendar data for %s in '%s' >>
172              
173             =back
174              
175              
176             =head1 AUTHOR
177              
178             Flavio Poletti C<< <flavio [at] polettix [dot] it> >>
179              
180              
181             =head1 LICENCE AND COPYRIGHT
182              
183             Copyright (c) 2008, Flavio Poletti C<< <flavio [at] polettix [dot] it> >>. All rights reserved.
184              
185             This module is free software; you can redistribute it and/or
186             modify it under the same terms as Perl 5.8.x itself. See L<perlartistic>
187             and L<perlgpl>.
188              
189             Questo modulo è software libero: potete ridistribuirlo e/o
190             modificarlo negli stessi termini di Perl 5.8.x stesso. Vedete anche
191             L<perlartistic> e L<perlgpl>.
192              
193              
194             =head1 DISCLAIMER OF WARRANTY
195              
196             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
197             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
198             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
199             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
200             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
201             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
202             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
203             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
204             NECESSARY SERVICING, REPAIR, OR CORRECTION.
205              
206             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
207             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
208             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
209             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
210             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
211             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
212             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
213             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
214             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
215             SUCH DAMAGES.
216              
217             =head1 NEGAZIONE DELLA GARANZIA
218              
219             Poiché questo software viene dato con una licenza gratuita, non
220             c'è alcuna garanzia associata ad esso, ai fini e per quanto permesso
221             dalle leggi applicabili. A meno di quanto possa essere specificato
222             altrove, il proprietario e detentore del copyright fornisce questo
223             software "così com'è" senza garanzia di alcun tipo, sia essa espressa
224             o implicita, includendo fra l'altro (senza però limitarsi a questo)
225             eventuali garanzie implicite di commerciabilità e adeguatezza per
226             uno scopo particolare. L'intero rischio riguardo alla qualità ed
227             alle prestazioni di questo software rimane a voi. Se il software
228             dovesse dimostrarsi difettoso, vi assumete tutte le responsabilità
229             ed i costi per tutti i necessari servizi, riparazioni o correzioni.
230              
231             In nessun caso, a meno che ciò non sia richiesto dalle leggi vigenti
232             o sia regolato da un accordo scritto, alcuno dei detentori del diritto
233             di copyright, o qualunque altra parte che possa modificare, o redistribuire
234             questo software così come consentito dalla licenza di cui sopra, potrà
235             essere considerato responsabile nei vostri confronti per danni, ivi
236             inclusi danni generali, speciali, incidentali o conseguenziali, derivanti
237             dall'utilizzo o dall'incapacità di utilizzo di questo software. Ciò
238             include, a puro titolo di esempio e senza limitarsi ad essi, la perdita
239             di dati, l'alterazione involontaria o indesiderata di dati, le perdite
240             sostenute da voi o da terze parti o un fallimento del software ad
241             operare con un qualsivoglia altro software. Tale negazione di garanzia
242             rimane in essere anche se i dententori del copyright, o qualsiasi altra
243             parte, è stata avvisata della possibilità di tali danneggiamenti.
244              
245             Se decidete di utilizzare questo software, lo fate a vostro rischio
246             e pericolo. Se pensate che i termini di questa negazione di garanzia
247             non si confacciano alle vostre esigenze, o al vostro modo di
248             considerare un software, o ancora al modo in cui avete sempre trattato
249             software di terze parti, non usatelo. Se lo usate, accettate espressamente
250             questa negazione di garanzia e la piena responsabilità per qualsiasi
251             tipo di danno, di qualsiasi natura, possa derivarne.
252              
253             =cut