From: Ian Beckwith Date: Wed, 18 Apr 2007 21:37:55 +0000 (+0100) Subject: new upstream 0.11 X-Git-Tag: 0.13-2~8 X-Git-Url: http://erislabs.net/gitweb/?p=libwww-opensearch-perl.git;a=commitdiff_plain;h=0418a266d571b99934f124a6d8e2b1d4b99f22fc new upstream 0.11 --- diff --git a/Changes b/Changes index b1df0f3..1a7cba1 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,34 @@ Revision history for Perl extension WWW::OpenSearch +0.11 Tue Apr 17 2007 + - added a simple OSD parsing test + +0.10_02 Tue Jan 23 2007 + - fix Request.pm to clone the extra attributes we've + added (Marc Tobias) + +0.10_01 Mon Jan 22 2007 + - use URI::Template for parsing OSD uri templates + - added opensearch-specific Request and Agent classes + - un-break get_best_url() + - you can now pass a WWW::OpenSearch::Url to WWW::OpenSearch's + search() method + - added ns() (namespace) field to Url.pm + - re-worked paging to use the new Request object + + [ THINGS THAT MAY BREAK YOUR CODE ] + - using URI::Template means some methods are now proxied to + that class + - removed agent argument in new() in OpenSearch.pm + - handling of POST requests in prepare_query() in Url.pm now + returns data suitable for passing to HTTP::Request + - un-link Response.pm and the parent WWW::OpenSearch object -- + a Response is now in the context of whatever + WWW::OpenSearch::Url was used + +0.09 Thu Dec 07 2006 + - fix link fetching from atom feeds + 0.08 Wed Sep 13 2006 - fix optional attributes for Image - added strict to Url object diff --git a/MANIFEST b/MANIFEST index f9d961c..1b195bf 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,18 +1,23 @@ Changes lib/WWW/OpenSearch.pm +lib/WWW/OpenSearch/Agent.pm lib/WWW/OpenSearch/Description.pm lib/WWW/OpenSearch/Image.pm lib/WWW/OpenSearch/Query.pm +lib/WWW/OpenSearch/Request.pm lib/WWW/OpenSearch/Response.pm lib/WWW/OpenSearch/Url.pm Makefile.PL -MANIFEST This list of files -META.yml Module meta-data (added by MakeMaker) +MANIFEST This list of files README t/00_compile.t t/01_live.t +t/09-opensearch.t t/10-description.t t/11-url.t t/12-response.t +t/13-request.t t/98_pod.t t/99_pod_coverage.t +t/data/osd.xml +META.yml Module meta-data (added by MakeMaker) diff --git a/META.yml b/META.yml index e6677ad..1bcc3a5 100644 --- a/META.yml +++ b/META.yml @@ -1,17 +1,21 @@ -# http://module-build.sourceforge.net/META-spec.html -#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# -name: WWW-OpenSearch -version: 0.08 -version_from: lib/WWW/OpenSearch.pm -installdirs: site -requires: +--- #YAML:1.0 +name: WWW-OpenSearch +version: 0.11 +abstract: Search A9 OpenSearch compatible engines +license: perl +generated_by: ExtUtils::MakeMaker version 6.32 +distribution_type: module +requires: Data::Page: 2 Encode: 0 LWP: 5.6 Test::More: 0.32 URI: 0 + URI::Template: 0 XML::Feed: 0.08 XML::LibXML: 1.58 - -distribution_type: module -generated_by: ExtUtils::MakeMaker version 6.17 +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.2.html + version: 1.2 +author: + - Brian Cassidy diff --git a/Makefile.PL b/Makefile.PL index b24fa45..fb44e76 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,14 +1,18 @@ use ExtUtils::MakeMaker; WriteMakefile( - 'NAME' => 'WWW::OpenSearch', - 'VERSION_FROM' => 'lib/WWW/OpenSearch.pm', + 'NAME' => 'WWW::OpenSearch', + 'VERSION_FROM' => 'lib/WWW/OpenSearch.pm', + 'ABSTRACT_FROM' => 'lib/WWW/OpenSearch.pm', + 'AUTHOR' => 'Brian Cassidy ', + 'LICENSE' => 'perl', 'PREREQ_PM' => { - 'Data::Page' => 2.00, - 'Encode' => 0, - 'LWP' => 5.60, - 'Test::More' => 0.32, - 'URI' => 0, - 'XML::Feed' => 0.08, - 'XML::LibXML' => 1.58 + 'Data::Page' => 2.00, + 'Encode' => 0, + 'LWP' => 5.60, + 'Test::More' => 0.32, + 'URI' => 0, + 'XML::Feed' => 0.08, + 'XML::LibXML' => 1.58, + 'URI::Template' => 0, }, ); diff --git a/README b/README index c77f030..2c3973c 100644 --- a/README +++ b/README @@ -1,69 +1,69 @@ -NAME - WWW::OpenSearch - Search A9 OpenSearch compatible engines - -SYNOPSIS - use WWW::OpenSearch; - - my $url = "http://bulkfeeds.net/opensearch.xml"; - my $engine = WWW::OpenSearch->new($url); - - my $name = $engine->description->ShortName; - my $tags = $engine->description->Tags; - - # Perform search for "iPod" - my $response = $engine->search("iPod"); - for my $item (@{$response->feed->items}) { - print $item->{description}; - } - - # Retrieve the next page of results - my $next_page = $response->next_page; - for my $item (@{$next_page->feed->items}) { - print $item->{description}; - } - -DESCRIPTION - WWW::OpenSearch is a module to search A9's OpenSearch compatible search - engines. See http://opensearch.a9.com/ for details. - -CONSTRUCTOR - new( $url ) - Constructs a new instance of WWW::OpenSearch using the given URL as the - location of the engine's OpenSearch Description document (retrievable - via the description_url accessor). - -METHODS - fetch_description( [ $url ] ) - Fetches the OpenSearch Descsription found either at the given URL or at - the URL specified by the description_url accessor. Fetched description - may be accessed via the description accessor. - - search( $query [, \%params] ) - Searches the engine for the given query using the given search - parameters. Valid search parameters include: - - * startPage - * totalResults - * startIndex - * itemsPerPage - - See http://opensearch.a9.com/spec/1.1/response/#elements for details. - - do_search( $url [, $method] ) - Performs a request for the given URL and returns a - WWW::OpenSearch::Response object. Method defaults to 'GET'. - -ACCESSORS - description_url( [$description_url] ) - agent( [$agent] ) - description( [$description] ) -AUTHOR - * Tatsuhiko Miyagawa - * Brian Cassidy - -COPYRIGHT AND LICENSE - Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy - - This library is free software; you can redistribute it and/or modify it - under the same terms as Perl itself. - +NAME + WWW::OpenSearch - Search A9 OpenSearch compatible engines + +SYNOPSIS + use WWW::OpenSearch; + + my $url = "http://bulkfeeds.net/opensearch.xml"; + my $engine = WWW::OpenSearch->new($url); + + my $name = $engine->description->ShortName; + my $tags = $engine->description->Tags; + + # Perform search for "iPod" + my $response = $engine->search("iPod"); + for my $item (@{$response->feed->items}) { + print $item->{description}; + } + + # Retrieve the next page of results + my $next_page = $response->next_page; + for my $item (@{$next_page->feed->items}) { + print $item->{description}; + } + +DESCRIPTION + WWW::OpenSearch is a module to search A9's OpenSearch compatible search + engines. See http://opensearch.a9.com/ for details. + +CONSTRUCTOR + new( $url ) + Constructs a new instance of WWW::OpenSearch using the given URL as the + location of the engine's OpenSearch Description document (retrievable + via the description_url accessor). + +METHODS + fetch_description( [ $url ] ) + Fetches the OpenSearch Descsription found either at the given URL or at + the URL specified by the description_url accessor. Fetched description + may be accessed via the description accessor. + + search( $query [, \%params] ) + Searches the engine for the given query using the given search + parameters. Valid search parameters include: + + * startPage + * totalResults + * startIndex + * itemsPerPage + + See http://opensearch.a9.com/spec/1.1/response/#elements for details. + + do_search( $url [, $method] ) + Performs a request for the given URL and returns a + WWW::OpenSearch::Response object. Method defaults to 'GET'. + +ACCESSORS + description_url( [$description_url] ) + agent( [$agent] ) + description( [$description] ) +AUTHOR + * Tatsuhiko Miyagawa + * Brian Cassidy + +COPYRIGHT AND LICENSE + Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy + + This library is free software; you can redistribute it and/or modify it + under the same terms as Perl itself. + diff --git a/lib/WWW/OpenSearch.pm b/lib/WWW/OpenSearch.pm index e1c0bc8..86ad45c 100644 --- a/lib/WWW/OpenSearch.pm +++ b/lib/WWW/OpenSearch.pm @@ -6,13 +6,15 @@ use warnings; use base qw( Class::Accessor::Fast ); use Carp; -use WWW::OpenSearch::Response; +use WWW::OpenSearch::Agent; +use WWW::OpenSearch::Request; use WWW::OpenSearch::Description; -use Encode qw( _utf8_off ); + +use Encode (); __PACKAGE__->mk_accessors( qw( description_url agent description ) ); -our $VERSION = '0.08'; +our $VERSION = '0.11'; =head1 NAME @@ -42,17 +44,16 @@ WWW::OpenSearch - Search A9 OpenSearch compatible engines =head1 DESCRIPTION -WWW::OpenSearch is a module to search A9's OpenSearch compatible search engines. See http://opensearch.a9.com/ for details. +WWW::OpenSearch is a module to search A9's OpenSearch compatible search +engines. See http://opensearch.a9.com/ for details. =head1 CONSTRUCTOR -=head2 new( $url [, $useragent] ) +=head2 new( $url ) Constructs a new instance of WWW::OpenSearch using the given URL as the location of the engine's OpenSearch Description -document (retrievable via the description_url accessor). Pass any -LWP::UserAgent compatible object if you wish to override the default -agent. +document (retrievable via the description_url accessor). =head1 METHODS @@ -106,7 +107,7 @@ WWW::OpenSearch::Response object. Method defaults to 'GET'. =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -114,19 +115,14 @@ it under the same terms as Perl itself. =cut sub new { - my( $class, $url, $agent ) = @_; + my( $class, $url ) = @_; croak( "No OpenSearch Description url provided" ) unless $url; my $self = $class->SUPER::new; - unless( $agent ) { - require LWP::UserAgent; - $agent = LWP::UserAgent->new( agent => join( '/', ref $self, $VERSION ) ); - } - $self->description_url( $url ); - $self->agent( $agent ); + $self->agent( WWW::OpenSearch::Agent->new() ); $self->fetch_description; @@ -147,30 +143,14 @@ sub fetch_description { } sub search { - my( $self, $query, $params ) = @_; + my( $self, $query, $params, $url ) = @_; $params ||= { }; $params->{ searchTerms } = $query; - _utf8_off( $params->{ searchTerms } ); - - my $url = $self->description->get_best_url; - return $self->do_search( $url->prepare_query( $params ), $url->method ); -} - -sub do_search { - my( $self, $url, $method ) = @_; - - $method = lc( $method ) || 'get'; - - my $response; - if( $method eq 'post' ) { - $response = $self->agent->post( @$url ); - } - else { - $response = $self->agent->$method( $url ); - } + Encode::_utf8_off( $params->{ searchTerms } ); - return WWW::OpenSearch::Response->new( $self, $response ); + $url ||= $self->description->get_best_url; + return $self->agent->search( WWW::OpenSearch::Request->new( $url, $params ) ); } 1; diff --git a/lib/WWW/OpenSearch/Agent.pm b/lib/WWW/OpenSearch/Agent.pm new file mode 100644 index 0000000..bc5bbec --- /dev/null +++ b/lib/WWW/OpenSearch/Agent.pm @@ -0,0 +1,68 @@ +package WWW::OpenSearch::Agent; + +use strict; +use warnings; + +use base qw( LWP::UserAgent ); + +use WWW::OpenSearch; +use WWW::OpenSearch::Response; + +=head1 NAME + +WWW::OpenSearch::Agent - An agent for doing OpenSearch requests + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +=head1 CONSTRUCTOR + +=head2 new( [%options] ) + +=head1 METHODS + +=head2 request( $request | $url, \%params ) + +=head2 search( ) + +An alias for request() + +=head1 AUTHOR + +=over 4 + +=item * Brian Cassidy Ebricas@cpan.orgE + +=back + +=head1 COPYRIGHT AND LICENSE + +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +sub new { + my( $class, @rest ) = @_; + return $class->SUPER::new( + agent => join( '/', __PACKAGE__, $WWW::OpenSearch::VERSION ), + @rest, + ); +} + +*search = \&request; + +sub request { + my $self = shift; + my $request = shift; ; + my $response = $self->SUPER::request( $request, @_ ); + + # allow regular HTTP::Requests to flow through + return $response unless $request->isa( 'WWW::OpenSearch::Request' ); + return WWW::OpenSearch::Response->new( $response ); +} + +1; diff --git a/lib/WWW/OpenSearch/Description.pm b/lib/WWW/OpenSearch/Description.pm index 807c576..0075e35 100644 --- a/lib/WWW/OpenSearch/Description.pm +++ b/lib/WWW/OpenSearch/Description.pm @@ -125,7 +125,7 @@ whose type is equal to $type. =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -165,12 +165,13 @@ sub load { my $ns = $element->getNamespace->value; my $version; - if( $ns eq 'http://a9.com/-/spec/opensearch/1.1/' ) { - $self->ns( $ns ); - $version = '1.1'; + if( $ns eq 'http://a9.com/-/spec/opensearchdescription/1.0/' ) { + $self->ns( 'http://a9.com/-/spec/opensearchrss/1.0/' ); + $version = '1.0'; } else { - $version = '1.0'; + $self->ns( $ns ); + ( $version ) = $ns =~ m{([^/]+)/?$}; } $self->version( $version ); @@ -178,7 +179,7 @@ sub load { my $node = $doc->documentElement->getChildrenByTagName( $column ) or next; if( $column eq 'Url' ) { if( $version eq '1.0' ) { - $self->Url( [ WWW::OpenSearch::Url->new( template => $node->string_value, type => 'application/rss+xml' ) ] ); + $self->Url( [ WWW::OpenSearch::Url->new( template => $node->string_value, type => 'application/rss+xml', ns => $self->ns ) ] ); next; } @@ -198,7 +199,7 @@ sub load { $params{ $param } = $value; } - push @url, WWW::OpenSearch::Url->new( template => $url, type => $type, method => $method, params => \%params ); + push @url, WWW::OpenSearch::Url->new( template => $url, type => $type, method => $method, params => \%params, ns => $self->ns ); } $self->Url( \@url ); } @@ -229,10 +230,6 @@ sub load { $self->image( $images ); } - elsif( $version eq '1.0' and $column eq 'Format' ) { - $self->Format( $node->string_value ); - $self->ns( $self->Format ); - } else { $self->$column( $node->string_value ); } @@ -252,13 +249,11 @@ sub get_url_by_type { my $self = shift; my $type = shift; - my $template; for( $self->urls ) { - $template = $_ if $_->type eq $type; - last; + return $_ if $_->type eq $type; }; - return $template; + return; } sub urls { diff --git a/lib/WWW/OpenSearch/Image.pm b/lib/WWW/OpenSearch/Image.pm index 945b58e..01dafe4 100644 --- a/lib/WWW/OpenSearch/Image.pm +++ b/lib/WWW/OpenSearch/Image.pm @@ -45,7 +45,7 @@ WWW::OpenSearch::Image - Object to represent an image =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff --git a/lib/WWW/OpenSearch/Query.pm b/lib/WWW/OpenSearch/Query.pm index 52d7eff..b75ba77 100644 --- a/lib/WWW/OpenSearch/Query.pm +++ b/lib/WWW/OpenSearch/Query.pm @@ -41,7 +41,7 @@ WWW::OpenSearch::Query - Object to represent a sample query =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. diff --git a/lib/WWW/OpenSearch/Request.pm b/lib/WWW/OpenSearch/Request.pm new file mode 100644 index 0000000..7c33255 --- /dev/null +++ b/lib/WWW/OpenSearch/Request.pm @@ -0,0 +1,85 @@ +package WWW::OpenSearch::Request; + +use strict; +use warnings; + +use base qw( HTTP::Request Class::Accessor::Fast ); + +use HTTP::Request::Common (); + +__PACKAGE__->mk_accessors( qw( opensearch_url opensearch_params ) ); + +=head1 NAME + +WWW::OpenSearch::Request - Encapsulate an opensearch request + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +=head1 CONSTRUCTOR + +=head2 new( $url, \%params ) + +=head1 METHODS + +=head2 clone( ) + +=head1 ACCESSORS + +=over 4 + +=item * opensearch_url + +=item * opensearch_params + +=back + +=head1 AUTHOR + +=over 4 + +=item * Brian Cassidy Ebricas@cpan.orgE + +=back + +=head1 COPYRIGHT AND LICENSE + +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +sub new { + my( $class, $os_url, $params ) = @_; + + my( $uri, $post ) = $os_url->prepare_query( $params ); + + my $self; + if( lc $os_url->method eq 'post' ) { + $self = HTTP::Request::Common::POST( $uri, $post ); + bless $self, $class; + } + else { + $self = $class->SUPER::new( $os_url->method => $uri ); + } + + $self->opensearch_url( $os_url ); + $self->opensearch_params( $params ); + + return $self; +} + +sub clone { + my $self = shift; + my $clone = bless $self->SUPER::clone, ref( $self ); + + $clone->opensearch_url( $self->opensearch_url ); + $clone->opensearch_params( $self->opensearch_params ); + + return $clone; +} + +1; diff --git a/lib/WWW/OpenSearch/Response.pm b/lib/WWW/OpenSearch/Response.pm index 816476a..d0b923c 100644 --- a/lib/WWW/OpenSearch/Response.pm +++ b/lib/WWW/OpenSearch/Response.pm @@ -6,10 +6,11 @@ use warnings; use base qw( HTTP::Response Class::Accessor::Fast ); use XML::Feed; -use URI; use Data::Page; +use WWW::OpenSearch::Agent; +use WWW::OpenSearch::Request; -__PACKAGE__->mk_accessors( qw( feed pager parent ) ); +__PACKAGE__->mk_accessors( qw( feed pager ) ); =head1 NAME @@ -43,11 +44,10 @@ See http://opensearch.a9.com/spec/1.1/response/ for details. =head1 CONSTRUCTOR -=head2 new( $parent, $response ) +=head2 new( $response ) -Constructs a new instance of WWW::OpenSearch::Response. Arguments -include the WWW::OpenSearch object which initiated the search (parent) -and the HTTP::Response returned by the search request. +Constructs a new instance of WWW::OpenSearch::Response from the +WWWW::OpenSearch:Response returned by the search request. =head1 METHODS @@ -82,8 +82,6 @@ is equal to $type. =head2 pager( ) -=head2 parent( ) - =head1 AUTHOR =over 4 @@ -96,7 +94,7 @@ is equal to $type. =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -105,12 +103,10 @@ it under the same terms as Perl itself. sub new { my $class = shift; - my $parent = shift; my $response = shift; my $self = bless $response, $class; - $self->parent( $parent ); return $self unless $self->is_success; $self->parse_response; @@ -136,7 +132,7 @@ sub parse_feed { my $feed = $self->feed; my $format = $feed->format; - my $ns = $self->parent->description->ns; + my $ns = $self->request->opensearch_url->ns; # TODO # adapt these for any number of opensearch elements in @@ -178,42 +174,56 @@ sub _get_page { my $pagermethod = "${direction}_page"; my $page = $pager->$pagermethod; return unless $page; - - my $request = $self->request; - my $method = lc $request->method; - if( $method ne 'post' ) { # force query build on POST - my $link = $self->_get_link( $direction ); - return $self->parent->do_search( $link, $method ) if $link; - } - - my $template = $self->parent->description->get_best_url; - my( $param, $query ); - if( $method eq 'post' ) { - my $uri = URI->new( 'http://foo.com/?' . $request->content ); - $query = { $uri->query_form }; - } - else { - $query = { $self->request->uri->query_form }; - } - - if( $param = $template->macros->{ startPage } ) { - $query->{ $param } = $pager->$pagermethod - } - elsif( $param = $template->macros->{ startIndex } ) { - if( $query->{ $param } ) { - $query->{ $param } = $direction eq 'previous' - ? $query->{ $param } -= $pager->entries_per_page - : $query->{ $param } += $pager->entries_per_page; - } - else { - $query->{ $param } = $direction eq 'previous' - ? 1 - : $pager->entries_per_page + 1; + my $params; + my $osu = $self->request->opensearch_url; + +# this code is too fragile -- deparse depends on the order of query +# params and the like. best just to use the last query params and +# do the paging from there. +# +# if( lc $osu->method ne 'post' ) { # force query build on POST +# my $link = $self->_get_link( $direction ); +# if( $link ) { +# $params = $osu->deparse( $link ); +# } +# } + + # rebuild the query + if( !$params ) { + $params = $self->request->opensearch_params; + + # handle paging via a page # + $params->{ startPage } = $page; + + # handle paging via an index + if( exists $params->{ startIndex } ) { + # start index is pre-existing + if( $params->{ startIndex } ) { + if( $direction eq 'previous' ) { + $params->{ startIndex } -= $pager->entries_per_page + } + else { + $params->{ startIndex } += $pager->entries_per_page; + } + } + # start index did not exist previously + else { + if( $direction eq 'previous' ) { + $params->{ startIndex } = 1 + } + else { + $params->{ startIndex } = $pager->entries_per_page + 1; + } + + } } } - return $self->parent->do_search( $template->prepare_query( $query ), $method ); + my $agent = WWW::OpenSearch::Agent->new; + return $agent->search( WWW::OpenSearch::Request->new( + $osu, $params + ) ); } sub _get_link { @@ -224,7 +234,7 @@ sub _get_link { return unless $feed; for( $feed->link ) { - return $_->get( 'href' ) if $_->get( 'rel' ) eq $type; + return $_->href if $_->rel eq $type; } return; diff --git a/lib/WWW/OpenSearch/Url.pm b/lib/WWW/OpenSearch/Url.pm index 5303930..6e4c27f 100644 --- a/lib/WWW/OpenSearch/Url.pm +++ b/lib/WWW/OpenSearch/Url.pm @@ -5,10 +5,9 @@ use warnings; use base qw( Class::Accessor::Fast ); -use URI; -use URI::Escape; +use URI::Template; -__PACKAGE__->mk_accessors( qw( type template method params macros ) ); +__PACKAGE__->mk_accessors( qw( type template method params ns ) ); =head1 NAME @@ -24,8 +23,6 @@ WWW::OpenSearch::Url - Object to represent a target URL =head1 METHODS -=head2 parse_macros( ) - =head2 prepare_query( [ \%params ] ) =head1 ACCESSORS @@ -40,7 +37,7 @@ WWW::OpenSearch::Url - Object to represent a target URL =item * params -=item * macros +=item * ns =back @@ -56,7 +53,7 @@ WWW::OpenSearch::Url - Object to represent a target URL =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy +Copyright 2007 by Tatsuhiko Miyagawa and Brian Cassidy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -67,34 +64,16 @@ sub new { my( $class, %options ) = @_; $options{ method } ||= 'GET'; - $options{ template } = URI->new( $options{ template } ); + $options{ template } = URI::Template->new( $options{ template } ); my $self = $class->SUPER::new( \%options ); - $self->parse_macros; return $self; } -sub parse_macros { - my $self = shift; - - my %query = $self->method eq 'post' - ? %{ $self->params } - : $self->template->query_form; - - my %macros; - for( keys %query ) { - if( $query{ $_ } =~ /^{(.+)}$/ ) { - $macros{ $1 } = $_; - } - } - - $self->macros( \%macros ); -} - sub prepare_query { my( $self, $params ) = @_; - my $url = $self->template->clone; + my $tmpl = $self->template; $params->{ startIndex } ||= 1; $params->{ startPage } ||= 1; @@ -102,23 +81,19 @@ sub prepare_query { $params->{ outputEncoding } ||= 'UTF-8'; $params->{ inputEncoding } ||= 'UTF-8'; - my $macros = $self->macros; + # fill the uri template + my $url = $tmpl->process( %$params ); # attempt to handle POST if( $self->method eq 'post' ) { my $post = $self->params; - for( keys %$macros ) { - $post->{ $macros->{ $_ } } = $params->{ $_ }; + for my $key ( keys %$post ) { + $post->{ $key } =~ s/{(.+)}/$params->{ $1 } || ''/eg; } - return [ $url, $post ]; - } - my $query = { $url->query_form }; - for( keys %$macros ) { - $query->{ $macros->{ $_ } } = $params->{ $_ }; + return $url, [ %$post ]; } - $url->query_form( $query ); return $url; } diff --git a/t/00_compile.t b/t/00_compile.t index 32a1ed5..0981f98 100644 --- a/t/00_compile.t +++ b/t/00_compile.t @@ -1,5 +1,5 @@ use strict; -use Test::More tests => 6; +use Test::More tests => 8; BEGIN { use_ok 'WWW::OpenSearch'; @@ -8,4 +8,6 @@ BEGIN { use_ok 'WWW::OpenSearch::Url'; use_ok 'WWW::OpenSearch::Query'; use_ok 'WWW::OpenSearch::Image'; + use_ok 'WWW::OpenSearch::Agent'; + use_ok 'WWW::OpenSearch::Request'; } diff --git a/t/09-opensearch.t b/t/09-opensearch.t new file mode 100644 index 0000000..ba34d26 --- /dev/null +++ b/t/09-opensearch.t @@ -0,0 +1,9 @@ +use Test::More tests => 2; + +use_ok( 'WWW::OpenSearch' ); +use URI::file; + +my $uri = URI::file->new_abs( 't/data/osd.xml' ); + +my $engine = WWW::OpenSearch->new( $uri ); +isa_ok( $engine, 'WWW::OpenSearch' ); diff --git a/t/10-description.t b/t/10-description.t index 4e23a8c..3eacf72 100644 --- a/t/10-description.t +++ b/t/10-description.t @@ -20,11 +20,11 @@ use_ok( 'WWW::OpenSearch::Description' ); my $osd = WWW::OpenSearch::Description->new( $description ); isa_ok( $osd, 'WWW::OpenSearch::Description' ); - is( $osd->shortname, 'Web Search' ); - ok( !defined $osd->longname ); - is( $osd->description, 'Use Example.com to search the Web.' ); - is( $osd->tags, 'example web' ); - is( $osd->contact, 'admin@example.com' ); + is( $osd->shortname, 'Web Search', 'shortname' ); + ok( !defined $osd->longname, 'longname' ); + is( $osd->description, 'Use Example.com to search the Web.', 'description' ); + is( $osd->tags, 'example web', 'tags' ); + is( $osd->contact, 'admin@example.com', 'contact' ); # count the urls is( $osd->urls, 1, 'number of url objects' ); @@ -68,36 +68,36 @@ use_ok( 'WWW::OpenSearch::Description' ); my $osd = WWW::OpenSearch::Description->new( $description ); isa_ok( $osd, 'WWW::OpenSearch::Description' ); - is( $osd->shortname, 'Web Search' ); - is( $osd->longname, 'Example.com Web Search' ); - is( $osd->description, 'Use Example.com to search the Web.' ); - is( $osd->tags, 'example web' ); - is( $osd->contact, 'admin@example.com' ); - is( $osd->developer, 'Example.com Development Team' ); + is( $osd->shortname, 'Web Search', 'shortname' ); + is( $osd->longname, 'Example.com Web Search', 'longname' ); + is( $osd->description, 'Use Example.com to search the Web.', 'description' ); + is( $osd->tags, 'example web', 'tags' ); + is( $osd->contact, 'admin@example.com', 'contact' ); + is( $osd->developer, 'Example.com Development Team', 'developer' ); is( $osd->attribution, ' Search data © 2005, Example.com, Inc., All Rights Reserved - ' ); - is( $osd->inputencoding, 'UTF-8' ); - is( $osd->outputencoding, 'UTF-8' ); - is( $osd->language, 'en-us' ); - is( $osd->adultcontent, 'false' ); - is( $osd->syndicationright, 'open' ); + ', 'attribution' ); + is( $osd->inputencoding, 'UTF-8', 'inputencoding' ); + is( $osd->outputencoding, 'UTF-8', 'outputencoding' ); + is( $osd->language, 'en-us', 'language' ); + is( $osd->adultcontent, 'false', 'adultcontent' ); + is( $osd->syndicationright, 'open', 'syndicationright' ); my $queries = $osd->query; is( scalar @$queries, 1, 'number of query objects' ); - is( $queries->[ 0 ]->role, 'example' ); - is( $queries->[ 0 ]->searchTerms, 'cat' ); + is( $queries->[ 0 ]->role, 'example', 'role' ); + is( $queries->[ 0 ]->searchTerms, 'cat', 'searchTerms' ); my $images = $osd->image; is( scalar @$images, 2, 'number of image objects' ); - is( $images->[ 0 ]->height, 64 ); - is( $images->[ 0 ]->width, 64 ); - is( $images->[ 0 ]->type, 'image/png' ); - is( $images->[ 0 ]->url, 'http://example.com/websearch.png' ); - is( $images->[ 1 ]->height, 16 ); - is( $images->[ 1 ]->width, 16 ); - is( $images->[ 1 ]->type, 'image/vnd.microsoft.icon' ); - is( $images->[ 1 ]->url, 'http://example.com/websearch.ico' ); + is( $images->[ 0 ]->height, 64, 'height' ); + is( $images->[ 0 ]->width, 64, 'width' ); + is( $images->[ 0 ]->type, 'image/png', 'content type' ); + is( $images->[ 0 ]->url, 'http://example.com/websearch.png', 'url' ); + is( $images->[ 1 ]->height, 16, 'height' ); + is( $images->[ 1 ]->width, 16, 'width' ); + is( $images->[ 1 ]->type, 'image/vnd.microsoft.icon', 'content type' ); + is( $images->[ 1 ]->url, 'http://example.com/websearch.ico', 'url' ); # count the urls is( $osd->urls, 3, 'number of url objects' ); @@ -127,21 +127,20 @@ use_ok( 'WWW::OpenSearch::Description' ); my $osd = WWW::OpenSearch::Description->new( $description ); isa_ok( $osd, 'WWW::OpenSearch::Description' ); - is( $osd->shortname, 'Electronics' ); - is( $osd->longname, 'Amazon Electronics' ); - is( $osd->description, 'Search for electronics on Amazon.com.' ); - is( $osd->tags, 'amazon electronics' ); - is( $osd->contact, 'dewitt@unto.net' ); - is( $osd->format, 'http://a9.com/-/spec/opensearchrss/1.0/' ); - is( $osd->image, 'http://www.unto.net/search/amazon_electronics.gif' ); - is( $osd->samplesearch, 'ipod' ); - is( $osd->developer, 'DeWitt Clinton' ); + is( $osd->shortname, 'Electronics', 'shortname' ); + is( $osd->longname, 'Amazon Electronics', 'longname' ); + is( $osd->description, 'Search for electronics on Amazon.com.', 'descrpiton' ); + is( $osd->tags, 'amazon electronics', 'tags' ); + is( $osd->contact, 'dewitt@unto.net', 'contact' ); + is( $osd->format, 'http://a9.com/-/spec/opensearchrss/1.0/', 'format' ); + is( $osd->image, 'http://www.unto.net/search/amazon_electronics.gif', 'image' ); + is( $osd->samplesearch, 'ipod', 'samplesearch' ); + is( $osd->developer, 'DeWitt Clinton', 'developer' ); is( $osd->attribution, 'Product and search data © 2005, Amazon, Inc., - All Rights Reserved' ); - is( $osd->syndicationright, 'open' ); - is( $osd->adultcontent, 'false' ); + All Rights Reserved', 'attribution' ); + is( $osd->syndicationright, 'open', 'syndicationright' ); + is( $osd->adultcontent, 'false', 'adultcontent' ); # count the urls - is( $osd->urls, 1 ); + is( $osd->urls, 1, 'urls' ); } - diff --git a/t/11-url.t b/t/11-url.t index 44ccb81..9b1e2d8 100644 --- a/t/11-url.t +++ b/t/11-url.t @@ -1,7 +1,7 @@ use strict; use warnings; -use Test::More tests => 27; +use Test::More tests => 36; use_ok( 'WWW::OpenSearch::Description' ); use_ok( 'WWW::OpenSearch::Url' ); @@ -16,13 +16,17 @@ use_ok( 'WWW::OpenSearch::Url' ); my $osd = WWW::OpenSearch::Description->new( $description ); isa_ok( $osd, 'WWW::OpenSearch::Description' ); - is( $osd->urls, 1 ); + is( $osd->version, '1.1', 'version' ); + is( $osd->ns, 'http://a9.com/-/spec/opensearch/1.1/', 'namespace' ); + is( $osd->urls, 1, 'number of urls' ); my( $url ) = $osd->urls; isa_ok( $url, 'WWW::OpenSearch::Url' ); - is( $url->type, 'application/rss+xml' ); - is( lc $url->method, 'get' ); - is( $url->template, 'http://example.com/?q=%7BsearchTerms%7D&pw=%7BstartPage%7D&format=rss' ); + is( $url->type, 'application/rss+xml', 'content type' ); + is( lc $url->method, 'get', 'method' ); + is( $url->template, 'http://example.com/?q={searchTerms}&pw={startPage}&format=rss', 'template' ); + my $result = $url->prepare_query( { searchTerms => 'x', startPage => 1 } ); + is( $result, 'http://example.com/?q=x&pw=1&format=rss', 'prepare_query' ); } { @@ -45,30 +49,36 @@ use_ok( 'WWW::OpenSearch::Url' ); my $osd = WWW::OpenSearch::Description->new( $description ); isa_ok( $osd, 'WWW::OpenSearch::Description' ); - is( $osd->urls, 3 ); + is( $osd->urls, 3, 'number of urls' ); + is( $osd->get_best_url, $osd->url->[ 1 ], 'get_best_url' ); { my $url = $osd->url->[ 0 ]; isa_ok( $url, 'WWW::OpenSearch::Url' ); - is( $url->type, 'application/rss+xml' ); - is( lc $url->method, 'get' ); - is( $url->template, 'http://example.com/?q=%7BsearchTerms%7D&pw=%7BstartPage%7D&format=rss' ); + is( $url->type, 'application/rss+xml', 'content type' ); + is( lc $url->method, 'get', 'method' ); + is( $url->template, 'http://example.com/?q={searchTerms}&pw={startPage}&format=rss', 'template' ); } { my $url = $osd->url->[ 1 ]; isa_ok( $url, 'WWW::OpenSearch::Url' ); - is( $url->type, 'application/atom+xml' ); - is( lc $url->method, 'get' ); - is( $url->template, 'http://example.com/?q=%7BsearchTerms%7D&pw=%7BstartPage%7D&format=atom' ); + is( $url->type, 'application/atom+xml', 'content type' ); + is( lc $url->method, 'get', 'method' ); + is( $url->template, 'http://example.com/?q={searchTerms}&pw={startPage}&format=atom', 'template' ); } { my $url = $osd->url->[ 2 ]; isa_ok( $url, 'WWW::OpenSearch::Url' ); - is( $url->type, 'text/html' ); - is( lc $url->method, 'post' ); - is( $url->template, 'https://intranet/search?format=html' ); + is( $url->type, 'text/html', 'content type' ); + is( lc $url->method, 'post', 'method' ); + is( $url->template, 'https://intranet/search?format=html', 'template' ); + is_deeply( $url->params, { s => '{searchTerms}', o => '{startIndex}', c => '{itemsPerPage}', l => '{language}' }, 'params' ); + my( $result, $post ) = $url->prepare_query( { searchTerms => 'x', startIndex => '1', itemsPerPage => 1, language => 'en' } ); + is( $result, 'https://intranet/search?format=html', 'prepare_query (uri)' ); + $post = { @$post }; + is_deeply( $post, { s => 'x', o => 1, c => 1, l => 'en' }, 'prepare_query (params)' ); } } @@ -81,11 +91,13 @@ use_ok( 'WWW::OpenSearch::Url' ); my $osd = WWW::OpenSearch::Description->new( $description ); isa_ok( $osd, 'WWW::OpenSearch::Description' ); - is( $osd->urls, 1 ); + is( $osd->version, '1.0', 'version' ); + is( $osd->ns, 'http://a9.com/-/spec/opensearchrss/1.0/', 'namespace' ); + is( $osd->urls, 1, 'number of urls' ); my( $url ) = $osd->urls; isa_ok( $url, 'WWW::OpenSearch::Url' ); - is( lc $url->method, 'get' ); - is( $url->template, 'http://www.unto.net/aws?q=%7BsearchTerms%7D&searchindex=Electronics&flavor=osrss&itempage=%7BstartPage%7D' ); + is( lc $url->method, 'get', 'method' ); + is( $url->template, 'http://www.unto.net/aws?q={searchTerms}&searchindex=Electronics&flavor=osrss&itempage={startPage}', 'template' ); } diff --git a/t/13-request.t b/t/13-request.t new file mode 100644 index 0000000..24c9396 --- /dev/null +++ b/t/13-request.t @@ -0,0 +1,43 @@ +use Test::More tests => 9; + +use strict; +use warnings; + +use_ok( 'WWW::OpenSearch::Description' ); +use_ok( 'WWW::OpenSearch::Request' ); + +{ + my $description = q( + + + + + + + + + + +); + + my $osd = WWW::OpenSearch::Description->new( $description ); + + { + my $req = WWW::OpenSearch::Request->new( $osd->url->[ 2 ], { searchTerms => 'iPod' } ); + isa_ok( $req, 'WWW::OpenSearch::Request' ); + is( lc $req->method, 'post', 'method' ); + is( $req->uri, 'https://intranet/search?format=html', 'uri' ); + is( $req->content, 'l=*&c=&s=iPod&o=1', 'content' ); + } + + { + my $req = WWW::OpenSearch::Request->new( $osd->url->[ 1 ], { searchTerms => 'iPod' } ); + isa_ok( $req, 'WWW::OpenSearch::Request' ); + is( lc $req->method, 'get', 'method' ); + is( $req->uri, 'http://example.com/?q=iPod&pw=1&format=atom', 'uri' ); + } +} diff --git a/t/data/osd.xml b/t/data/osd.xml new file mode 100644 index 0000000..7ccafde --- /dev/null +++ b/t/data/osd.xml @@ -0,0 +1,31 @@ + + Web Search + Use Example.com to search the Web. + example web + admin@example.com + + + + + + + + + Example.com Web Search + http://example.com/websearch.png + http://example.com/websearch.ico + + Example.com Development Team + + Search data © 2005, Example.com, Inc., All Rights Reserved + + open + false + en-us + UTF-8 + UTF-8 +