new upstream 0.11
authorIan Beckwith <ianb@nessie.mcc.ac.uk>
Wed, 18 Apr 2007 21:37:55 +0000 (22:37 +0100)
committerIan Beckwith <ianb@nessie.mcc.ac.uk>
Wed, 18 Apr 2007 21:37:55 +0000 (22:37 +0100)
19 files changed:
Changes
MANIFEST
META.yml
Makefile.PL
README
lib/WWW/OpenSearch.pm
lib/WWW/OpenSearch/Agent.pm [new file with mode: 0644]
lib/WWW/OpenSearch/Description.pm
lib/WWW/OpenSearch/Image.pm
lib/WWW/OpenSearch/Query.pm
lib/WWW/OpenSearch/Request.pm [new file with mode: 0644]
lib/WWW/OpenSearch/Response.pm
lib/WWW/OpenSearch/Url.pm
t/00_compile.t
t/09-opensearch.t [new file with mode: 0644]
t/10-description.t
t/11-url.t
t/13-request.t [new file with mode: 0644]
t/data/osd.xml [new file with mode: 0644]

diff --git a/Changes b/Changes
index b1df0f3..1a7cba1 100644 (file)
--- 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
index f9d961c..1b195bf 100644 (file)
--- 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\r
-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)
index e6677ad..1bcc3a5 100644 (file)
--- 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 <bricas@cpan.org>
index b24fa45..fb44e76 100644 (file)
@@ -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 <bricas@cpan.org>',
+    '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 (file)
--- a/README
+++ b/README
@@ -1,69 +1,69 @@
-NAME\r
-    WWW::OpenSearch - Search A9 OpenSearch compatible engines\r
-\r
-SYNOPSIS\r
-        use WWW::OpenSearch;\r
-    \r
-        my $url = "http://bulkfeeds.net/opensearch.xml";\r
-        my $engine = WWW::OpenSearch->new($url);\r
-    \r
-        my $name = $engine->description->ShortName;\r
-        my $tags = $engine->description->Tags;\r
-    \r
-        # Perform search for "iPod"\r
-        my $response = $engine->search("iPod");\r
-        for my $item (@{$response->feed->items}) {\r
-            print $item->{description};\r
-        }\r
-    \r
-        # Retrieve the next page of results\r
-        my $next_page = $response->next_page;\r
-        for my $item (@{$next_page->feed->items}) {\r
-            print $item->{description};\r
-        }\r
-\r
-DESCRIPTION\r
-    WWW::OpenSearch is a module to search A9's OpenSearch compatible search\r
-    engines. See http://opensearch.a9.com/ for details.\r
-\r
-CONSTRUCTOR\r
-  new( $url )\r
-    Constructs a new instance of WWW::OpenSearch using the given URL as the\r
-    location of the engine's OpenSearch Description document (retrievable\r
-    via the description_url accessor).\r
-\r
-METHODS\r
-  fetch_description( [ $url ] )\r
-    Fetches the OpenSearch Descsription found either at the given URL or at\r
-    the URL specified by the description_url accessor. Fetched description\r
-    may be accessed via the description accessor.\r
-\r
-  search( $query [, \%params] )\r
-    Searches the engine for the given query using the given search\r
-    parameters. Valid search parameters include:\r
-\r
-    * startPage\r
-    * totalResults\r
-    * startIndex\r
-    * itemsPerPage\r
-\r
-    See http://opensearch.a9.com/spec/1.1/response/#elements for details.\r
-\r
-  do_search( $url [, $method] )\r
-    Performs a request for the given URL and returns a\r
-    WWW::OpenSearch::Response object. Method defaults to 'GET'.\r
-\r
-ACCESSORS\r
-  description_url( [$description_url] )\r
-  agent( [$agent] )\r
-  description( [$description] )\r
-AUTHOR\r
-    * Tatsuhiko Miyagawa <miyagawa@bulknews.net>\r
-    * Brian Cassidy <bricas@cpan.org>\r
-\r
-COPYRIGHT AND LICENSE\r
-    Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy\r
-\r
-    This library is free software; you can redistribute it and/or modify it\r
-    under the same terms as Perl itself.\r
-\r
+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 <miyagawa@bulknews.net>
+    * Brian Cassidy <bricas@cpan.org>
+
+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.
+
index e1c0bc8..86ad45c 100644 (file)
@@ -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 (file)
index 0000000..bc5bbec
--- /dev/null
@@ -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 E<lt>bricas@cpan.orgE<gt>
+
+=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;
index 807c576..0075e35 100644 (file)
@@ -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 {
index 945b58e..01dafe4 100644 (file)
@@ -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. 
index 52d7eff..b75ba77 100644 (file)
@@ -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 (file)
index 0000000..7c33255
--- /dev/null
@@ -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 E<lt>bricas@cpan.orgE<gt>
+
+=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;
index 816476a..d0b923c 100644 (file)
@@ -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;
index 5303930..6e4c27f 100644 (file)
@@ -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;
 }
 
index 32a1ed5..0981f98 100644 (file)
@@ -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 (file)
index 0000000..ba34d26
--- /dev/null
@@ -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' );
index 4e23a8c..3eacf72 100644 (file)
@@ -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 &copy; 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 &copy; 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' );
 }
-
index 44ccb81..9b1e2d8 100644 (file)
@@ -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 (file)
index 0000000..24c9396
--- /dev/null
@@ -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(<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+  <Url type="application/rss+xml"
+       template="http://example.com/?q={searchTerms}&amp;pw={startPage}&amp;format=rss"/>
+  <Url type="application/atom+xml"
+       template="http://example.com/?q={searchTerms}&amp;pw={startPage?}&amp;format=atom"/>
+  <Url type="text/html" 
+       method="post"
+       template="https://intranet/search?format=html">
+    <Param name="s" value="{searchTerms}"/>
+    <Param name="o" value="{startIndex?}"/>
+    <Param name="c" value="{itemsPerPage?}"/>
+    <Param name="l" value="{language?}"/>
+  </Url>
+</OpenSearchDescription>
+);
+
+    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 (file)
index 0000000..7ccafde
--- /dev/null
@@ -0,0 +1,31 @@
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+  <ShortName>Web Search</ShortName>
+  <Description>Use Example.com to search the Web.</Description>
+  <Tags>example web</Tags>
+  <Contact>admin@example.com</Contact>
+  <Url type="application/rss+xml"
+       template="http://example.com/?q={searchTerms}&amp;pw={startPage}&amp;format=rss"/>
+  <Url type="application/atom+xml"
+       template="http://example.com/?q={searchTerms}&amp;pw={startPage?}&amp;format=atom"/>
+  <Url type="text/html" 
+       method="post"
+       template="https://intranet/search?format=html">
+    <Param name="s" value="{searchTerms}"/>
+    <Param name="o" value="{startIndex?}"/>
+    <Param name="c" value="{itemsPerPage?}"/>
+    <Param name="l" value="{language?}"/>
+  </Url>
+  <LongName>Example.com Web Search</LongName>
+  <Image height="64" width="64" type="image/png">http://example.com/websearch.png</Image>
+  <Image height="16" width="16" type="image/vnd.microsoft.icon">http://example.com/websearch.ico</Image>
+  <Query role="example" searchTerms="cat" />
+  <Developer>Example.com Development Team</Developer>
+  <Attribution>
+    Search data &amp;copy; 2005, Example.com, Inc., All Rights Reserved
+  </Attribution>
+  <SyndicationRight>open</SyndicationRight>
+  <AdultContent>false</AdultContent>
+  <Language>en-us</Language>
+  <OutputEncoding>UTF-8</OutputEncoding>
+  <InputEncoding>UTF-8</InputEncoding>
+</OpenSearchDescription>