File Coverage

blib/lib/WWW/OpenSearch/Response.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::OpenSearch::Response;
2              
3 4     4   929 use strict;
  4         17  
  4         151  
4 4     4   20 use warnings;
  4         6  
  4         142  
5              
6 4     4   20 use base qw( HTTP::Response Class::Accessor::Fast );
  4         6  
  4         1825  
7              
8 4     4   61193 use XML::Feed;
  0            
  0            
9             use Data::Page;
10             use WWW::OpenSearch::Agent;
11             use WWW::OpenSearch::Request;
12              
13             __PACKAGE__->mk_accessors( qw( feed pager ) );
14              
15             =head1 NAME
16              
17             WWW::OpenSearch::Response - Encapsulate a response received from
18             an A9 OpenSearch compatible engine
19              
20             =head1 SYNOPSIS
21            
22             use WWW::OpenSearch;
23            
24             my $url = "http://bulkfeeds.net/opensearch.xml";
25             my $engine = WWW::OpenSearch->new($url);
26            
27             # Retrieve page 4 of search results for "iPod"
28             my $response = $engine->search("iPod",{ startPage => 4 });
29             for my $item (@{$response->feed->items}) {
30             print $item->{description};
31             }
32            
33             # Retrieve page 3 of results
34             $response = $response->previous_page;
35            
36             # Retrieve page 5 of results
37             $response = $response->next_page;
38            
39             =head1 DESCRIPTION
40              
41             WWW::OpenSearch::Response is a module designed to encapsulate a
42             response received from an A9 OpenSearch compatible engine.
43             See http://opensearch.a9.com/spec/1.1/response/ for details.
44              
45             =head1 CONSTRUCTOR
46              
47             =head2 new( $response )
48              
49             Constructs a new instance of WWW::OpenSearch::Response from the
50             WWWW::OpenSearch:Response returned by the search request.
51              
52             =head1 METHODS
53              
54             =head2 parse_response( )
55              
56             Parses the content of the HTTP response using XML::Feed. If successful,
57             parse_feed( ) is also called.
58              
59             =head2 parse_feed( )
60              
61             Parses the XML::Feed originally parsed from the HTTP response content.
62             Sets the pager object appropriately.
63              
64             =head2 previous_page( ) / next_page( )
65              
66             Performs another search on the parent object, returning a
67             WWW::OpenSearch::Response instance containing the previous/next page
68             of results. If the current response includes a <link rel="previous/next"
69             href="..." /> tag, the page will simply be the parsed content of the URL
70             specified by the tag's href attribute. However, if the current response does not
71             include the appropriate link, a new query is constructed using the startPage
72             or startIndex query arguments.
73              
74             =head2 _get_link( $type )
75              
76             Gets the href attribute of the first link whose rel attribute
77             is equal to $type.
78              
79             =head1 ACCESSORS
80              
81             =head2 feed( )
82              
83             =head2 pager( )
84              
85             =head1 AUTHOR
86              
87             =over 4
88              
89             =item * Tatsuhiko Miyagawa Emiyagawa@bulknews.netE
90              
91             =item * Brian Cassidy Ebricas@cpan.orgE
92              
93             =back
94              
95             =head1 COPYRIGHT AND LICENSE
96              
97             Copyright 2005-2013 by Tatsuhiko Miyagawa and Brian Cassidy
98              
99             This library is free software; you can redistribute it and/or modify
100             it under the same terms as Perl itself.
101              
102             =cut
103              
104             sub new {
105             my $class = shift;
106             my $response = shift;
107              
108             my $self = bless $response, $class;
109              
110             return $self unless $self->is_success;
111              
112             $self->parse_response;
113              
114             return $self;
115             }
116              
117             sub parse_response {
118             my $self = shift;
119              
120             my $content = $self->content;
121             my $feed = XML::Feed->parse( \$content );
122              
123             return if XML::Feed->errstr;
124             $self->feed( $feed );
125              
126             $self->parse_feed;
127             }
128              
129             sub parse_feed {
130             my $self = shift;
131             my $pager = Data::Page->new;
132              
133             my $feed = $self->feed;
134             my $format = $feed->format;
135             my $ns = $self->request->opensearch_url->ns;
136              
137             # TODO
138             # adapt these for any number of opensearch elements in
139             # the feed or in each entry
140              
141             if ( my $atom = $feed->{ atom } ) {
142             my $total = $atom->get( $ns, 'totalResults' );
143             my $perpage = $atom->get( $ns, 'itemsPerPage' );
144             my $start = $atom->get( $ns, 'startIndex' );
145              
146             $pager->total_entries( $total );
147             $pager->entries_per_page( $perpage );
148             $pager->current_page( $start ? ( $start - 1 ) / $perpage + 1 : 0 );
149             }
150             elsif ( my $rss = $feed->{ rss } ) {
151             if ( my $page = $rss->channel->{ $ns } ) {
152             $pager->total_entries( $page->{ totalResults } );
153             $pager->entries_per_page( $page->{ itemsPerPage } );
154             my $start = $page->{ startIndex };
155             $pager->current_page(
156             $start ? ( $start - 1 ) / $page->{ itemsPerPage } + 1 : 0 );
157             }
158             }
159             $self->pager( $pager );
160             }
161              
162             sub next_page {
163             my $self = shift;
164             return $self->_get_page( 'next' );
165             }
166              
167             sub previous_page {
168             my $self = shift;
169             return $self->_get_page( 'previous' );
170             }
171              
172             sub _get_page {
173             my ( $self, $direction ) = @_;
174             my $pager = $self->pager;
175             my $pagermethod = "${direction}_page";
176             my $page = $pager->$pagermethod;
177             return unless $page;
178              
179             my $params;
180             my $osu = $self->request->opensearch_url;
181              
182             # this code is too fragile -- deparse depends on the order of query
183             # params and the like. best just to use the last query params and
184             # do the paging from there.
185             #
186             # if( lc $osu->method ne 'post' ) { # force query build on POST
187             # my $link = $self->_get_link( $direction );
188             # if( $link ) {
189             # $params = $osu->deparse( $link );
190             # }
191             # }
192              
193             # rebuild the query
194             if ( !$params ) {
195             $params = $self->request->opensearch_params;
196              
197             # handle paging via a page #
198             $params->{ startPage } = $page;
199              
200             # handle paging via an index
201             if ( exists $params->{ startIndex } ) {
202              
203             # start index is pre-existing
204             if ( $params->{ startIndex } ) {
205             if ( $direction eq 'previous' ) {
206             $params->{ startIndex } -= $pager->entries_per_page;
207             }
208             else {
209             $params->{ startIndex } += $pager->entries_per_page;
210             }
211             }
212              
213             # start index did not exist previously
214             else {
215             if ( $direction eq 'previous' ) {
216             $params->{ startIndex } = 1;
217             }
218             else {
219             $params->{ startIndex } = $pager->entries_per_page + 1;
220             }
221              
222             }
223             }
224             }
225              
226             my $agent = WWW::OpenSearch::Agent->new;
227             return $agent->search( WWW::OpenSearch::Request->new( $osu, $params ) );
228             }
229              
230             sub _get_link {
231             my $self = shift;
232             my $type = shift;
233             my $feed = $self->feed->{ atom };
234              
235             return unless $feed;
236              
237             for ( $feed->link ) {
238             return $_->href if $_->rel eq $type;
239             }
240              
241             return;
242             }
243              
244             1;