root/WWW-OpenSearch/branches/WWW-OpenSearch-rewrite/lib/WWW/OpenSearch/Response.pm

Revision 1885 (checked in by bricas, 14 years ago)

updated Changes
removed check for "null"
bumped version

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