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