import of 0.08
authorianb <ianb>
Wed, 20 Sep 2006 00:08:18 +0000 (01:08 +0100)
committerianb <ianb>
Wed, 20 Sep 2006 00:08:18 +0000 (01:08 +0100)
Changes
MANIFEST
META.yml
lib/WWW/OpenSearch.pm
lib/WWW/OpenSearch/Description.pm
lib/WWW/OpenSearch/Image.pm [new file with mode: 0644]
lib/WWW/OpenSearch/Query.pm [new file with mode: 0644]
lib/WWW/OpenSearch/Response.pm
lib/WWW/OpenSearch/Url.pm
t/00_compile.t
t/10-description.t

diff --git a/Changes b/Changes
index 066bf90..b1df0f3 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,13 @@
 Revision history for Perl extension WWW::OpenSearch
 
+0.08    Wed Sep 13 2006
+        - fix optional attributes for Image
+        - added strict to Url object
+
+0.07    Mon Sep 11 2006
+        - inflate query and image fields to objects for OSD 1.1
+        - pod updates
+
 0.06_02 Thu Jul 20 2006
         - added tests
         - updated available description info
index 86e744e..f9d961c 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,6 +1,8 @@
 Changes
 lib/WWW/OpenSearch.pm
 lib/WWW/OpenSearch/Description.pm
+lib/WWW/OpenSearch/Image.pm
+lib/WWW/OpenSearch/Query.pm
 lib/WWW/OpenSearch/Response.pm
 lib/WWW/OpenSearch/Url.pm
 Makefile.PL
index 150d3aa..e6677ad 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -1,7 +1,7 @@
 # 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.06_02
+version:      0.08
 version_from: lib/WWW/OpenSearch.pm
 installdirs:  site
 requires:
index 5985929..e1c0bc8 100644 (file)
-package WWW::OpenSearch;\r
-\r
-use strict;\r
-use warnings;\r
-\r
-use base qw( Class::Accessor::Fast );\r
-\r
-use Carp;\r
-use WWW::OpenSearch::Response;\r
-use WWW::OpenSearch::Description;\r
-use Encode qw( _utf8_off ); \r
-\r
-__PACKAGE__->mk_accessors( qw( description_url agent description ) );\r
-\r
-our $VERSION = '0.06_02';\r
-\r
-=head1 NAME\r
-\r
-WWW::OpenSearch - Search A9 OpenSearch compatible engines\r
-\r
-=head1 SYNOPSIS\r
-\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
-=head1 DESCRIPTION\r
-\r
-WWW::OpenSearch is a module to search A9's OpenSearch compatible search engines. See http://opensearch.a9.com/ for details.\r
-\r
-=head1 CONSTRUCTOR\r
-\r
-=head2 new( $url [, $useragent] )\r
-\r
-Constructs a new instance of WWW::OpenSearch using the given\r
-URL as the location of the engine's OpenSearch Description\r
+package WWW::OpenSearch;
+
+use strict;
+use warnings;
+
+use base qw( Class::Accessor::Fast );
+
+use Carp;
+use WWW::OpenSearch::Response;
+use WWW::OpenSearch::Description;
+use Encode qw( _utf8_off ); 
+
+__PACKAGE__->mk_accessors( qw( description_url agent description ) );
+
+our $VERSION = '0.08';
+
+=head1 NAME
+
+WWW::OpenSearch - Search A9 OpenSearch compatible engines
+
+=head1 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};
+    }
+
+=head1 DESCRIPTION
+
+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] )
+
+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.\r
-\r
-=head1 METHODS\r
-\r
-=head2 fetch_description( [ $url ] )\r
-\r
-Fetches the OpenSearch Descsription found either at the given URL\r
-or at the URL specified by the description_url accessor. Fetched\r
-description may be accessed via the description accessor.\r
-\r
-=head2 search( $query [, \%params] )\r
-\r
-Searches the engine for the given query using the given \r
-search parameters. Valid search parameters include:\r
-\r
-=over 4\r
-\r
-=item * startPage\r
-\r
-=item * totalResults\r
-\r
-=item * startIndex\r
-\r
-=item * itemsPerPage\r
-\r
-=back\r
-\r
-See http://opensearch.a9.com/spec/1.1/response/#elements for details.\r
-\r
-=head2 do_search( $url [, $method] )\r
-\r
-Performs a request for the given URL and returns a\r
-WWW::OpenSearch::Response object. Method defaults to 'GET'.\r
-\r
-=head1 ACCESSORS\r
-\r
-=head2 description_url( [$description_url] )\r
-\r
-=head2 agent( [$agent] )\r
-\r
-=head2 description( [$description] )\r
-\r
-=head1 AUTHOR\r
-\r
-=over 4\r
-\r
-=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>\r
-\r
-=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>\r
-\r
-=back\r
-\r
-=head1 COPYRIGHT AND LICENSE\r
-\r
-Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy\r
-\r
-This library is free software; you can redistribute it and/or modify\r
-it under the same terms as Perl itself. \r
-\r
-=cut\r
-\r
-sub new {\r
-    my( $class, $url, $agent ) = @_;\r
-    \r
-    croak( "No OpenSearch Description url provided" ) unless $url;\r
-    \r
+agent.
+
+=head1 METHODS
+
+=head2 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.
+
+=head2 search( $query [, \%params] )
+
+Searches the engine for the given query using the given 
+search parameters. Valid search parameters include:
+
+=over 4
+
+=item * startPage
+
+=item * totalResults
+
+=item * startIndex
+
+=item * itemsPerPage
+
+=back
+
+See http://opensearch.a9.com/spec/1.1/response/#elements for details.
+
+=head2 do_search( $url [, $method] )
+
+Performs a request for the given URL and returns a
+WWW::OpenSearch::Response object. Method defaults to 'GET'.
+
+=head1 ACCESSORS
+
+=head2 description_url( [$description_url] )
+
+=head2 agent( [$agent] )
+
+=head2 description( [$description] )
+
+=head1 AUTHOR
+
+=over 4
+
+=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
+
+=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
+
+=back
+
+=head1 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. 
+
+=cut
+
+sub new {
+    my( $class, $url, $agent ) = @_;
+    
+    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 ) );
     }
-\r
-    $self->description_url( $url );\r
-    $self->agent( $agent );\r
-\r
-    $self->fetch_description;\r
-    \r
-    return $self;\r
-}\r
-\r
-sub fetch_description {\r
-    my( $self, $url ) = @_;\r
-    $url ||= $self->description_url;\r
-    $self->description_url( $url );\r
-    my $response = $self->agent->get( $url );\r
-    \r
-    unless( $response->is_success ) {\r
-        croak "Error while fetching $url: " . $response->status_line;\r
-    }\r
-\r
-    $self->description( WWW::OpenSearch::Description->new( $response->content ) );\r
-}\r
-\r
-sub search {\r
-    my( $self, $query, $params ) = @_;\r
-\r
-    $params ||= { };\r
-    $params->{ searchTerms } = $query;\r
-    _utf8_off( $params->{ searchTerms } ); \r
-    \r
-    my $url = $self->description->get_best_url;\r
-    return $self->do_search( $url->prepare_query( $params ), $url->method );\r
-}\r
-\r
-sub do_search {\r
-    my( $self, $url, $method ) = @_;\r
-    \r
-    $method = lc( $method ) || 'get';\r
-    \r
-    my $response;\r
-    if( $method eq 'post' ) {\r
-        $response = $self->agent->post( @$url );\r
-    }\r
-    else {\r
-        $response = $self->agent->$method( $url );\r
-    }\r
-    \r
-    return WWW::OpenSearch::Response->new( $self, $response );    \r
-}\r
-\r
-1;\r
+
+    $self->description_url( $url );
+    $self->agent( $agent );
+
+    $self->fetch_description;
+    
+    return $self;
+}
+
+sub fetch_description {
+    my( $self, $url ) = @_;
+    $url ||= $self->description_url;
+    $self->description_url( $url );
+    my $response = $self->agent->get( $url );
+    
+    unless( $response->is_success ) {
+        croak "Error while fetching $url: " . $response->status_line;
+    }
+
+    $self->description( WWW::OpenSearch::Description->new( $response->content ) );
+}
+
+sub search {
+    my( $self, $query, $params ) = @_;
+
+    $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 );
+    }
+    
+    return WWW::OpenSearch::Response->new( $self, $response );    
+}
+
+1;
index 6ed1e9a..807c576 100644 (file)
-package WWW::OpenSearch::Description;\r
-\r
-use strict;\r
-use warnings;\r
-\r
-use base qw( Class::Accessor::Fast );\r
-\r
-use Carp;\r
-use XML::LibXML;\r
-use WWW::OpenSearch::Url;\r
-\r
-my @columns = qw(\r
-    AdultContent Contact     Description      Developer\r
-    Format       Image       LongName         Query\r
-    SampleSearch ShortName   SyndicationRight Tags\r
+package WWW::OpenSearch::Description;
+
+use strict;
+use warnings;
+
+use base qw( Class::Accessor::Fast );
+
+use Carp;
+use XML::LibXML;
+use WWW::OpenSearch::Url;
+use WWW::OpenSearch::Query;
+use WWW::OpenSearch::Image;
+
+my @columns = qw(
+    AdultContent Contact     Description      Developer
+    Format       Image       LongName         Query
+    SampleSearch ShortName   SyndicationRight Tags
     Url          Attribution InputEncoding    OutputEncoding
-    Language\r
-);\r
-\r
-__PACKAGE__->mk_accessors( qw( version ns ), map { lc } @columns );\r
-\r
-=head1 NAME\r
-\r
-WWW::OpenSearch::Description - Encapsulate an OpenSearch Description\r
-provided by an A9 OpenSearch compatible engine\r
-\r
-=head1 SYNOPSIS\r
-    \r
-    use WWW::OpenSearch;\r
-    \r
-    my $url = "http://bulkfeeds.net/opensearch.xml";\r
-    my $engine = WWW::OpenSearch->new($url);\r
-    my $description = $engine->description;\r
-    \r
-    my $format   = $description->Format;   # or $description->format\r
-    my $longname = $description->LongName; # or $description->longname\r
-    \r
-=head1 DESCRIPTION\r
-\r
-WWW::OpenSearch::Description is a module designed to encapsulate an\r
-OpenSearch Description provided by an A9 OpenSearch compatible engine.\r
-See http://opensearch.a9.com/spec/1.1/description/ for details.\r
-\r
-=head1 CONSTRUCTOR\r
-\r
-=head2 new( [ $xml ] )\r
-\r
-Constructs a new instance of WWW::OpenSearch::Description. If scalar\r
-parameter $xml is provided, data will be automatically loaded from it\r
-using load( $xml ).\r
-\r
-=head1 METHODS\r
-\r
-=head2 load( $xml )\r
-\r
+    Language
+);
+
+__PACKAGE__->mk_accessors( qw( version ns ), map { lc } @columns );
+
+=head1 NAME
+
+WWW::OpenSearch::Description - Encapsulate an OpenSearch Description
+provided by an A9 OpenSearch compatible engine
+
+=head1 SYNOPSIS
+    
+    use WWW::OpenSearch;
+    
+    my $url = "http://bulkfeeds.net/opensearch.xml";
+    my $engine = WWW::OpenSearch->new($url);
+    my $description = $engine->description;
+    
+    my $format   = $description->Format;   # or $description->format
+    my $longname = $description->LongName; # or $description->longname
+    
+=head1 DESCRIPTION
+
+WWW::OpenSearch::Description is a module designed to encapsulate an
+OpenSearch Description provided by an A9 OpenSearch compatible engine.
+See http://opensearch.a9.com/spec/1.1/description/ for details.
+
+=head1 CONSTRUCTOR
+
+=head2 new( [ $xml ] )
+
+Constructs a new instance of WWW::OpenSearch::Description. If scalar
+parameter $xml is provided, data will be automatically loaded from it
+using load( $xml ).
+
+=head1 METHODS
+
+=head2 load( $xml )
+
 Loads description data by parsing provided argument using XML::LibXML.
 
 =head2 urls( )
 
-Return all of the urls associated with this description in an array.\r
-\r
-=head2 get_best_url( )\r
-\r
-Attempts to retrieve the best URL associated with this description, based\r
-on the following content types (from most preferred to least preferred):\r
-\r
-=over 4\r
-\r
-=item * application/atom+xml\r
-\r
-=item * application/rss+xml\r
-\r
-=item * text/xml\r
-\r
-=back\r
-\r
-=head2 get_url_by_type( $type )\r
-\r
-Retrieves the first WWW::OpenSearch::URL associated with this description\r
-whose type is equal to $type.\r
-\r
-=head1 ACCESSORS\r
-\r
-=head2 version( )\r
-\r
-=head2 ns( )\r
-\r
-=head2 AdultContent( )\r
-\r
-=head2 Contact( )\r
-\r
-=head2 Description( )\r
-\r
-=head2 Developer( )\r
-\r
-=head2 Format( )\r
-\r
-=head2 Image( )\r
-\r
-=head2 LongName( )\r
-\r
-=head2 Query( )\r
-\r
-=head2 SampleSearch( )\r
-\r
-=head2 ShortName( )\r
-\r
-=head2 SyndicationRight( )\r
-\r
-=head2 Tags( )\r
-\r
-=head2 Url( )\r
-\r
-=head1 AUTHOR\r
-\r
-=over 4\r
-\r
-=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>\r
-\r
-=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>\r
-\r
-=back\r
-\r
-=head1 COPYRIGHT AND LICENSE\r
-\r
-Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy\r
-\r
-This library is free software; you can redistribute it and/or modify\r
-it under the same terms as Perl itself. \r
-\r
-=cut\r
-\r
-for( @columns ) {\r
-    no strict 'refs';\r
-    my $col = lc;\r
-    *$_ = \&$col;\r
-}\r
-\r
-sub new {\r
-    my $class = shift;\r
-    my $xml   = shift;\r
-    \r
-    my $self  = $class->SUPER::new;\r
-    \r
-    eval{ $self->load( $xml ); } if $xml;\r
-    if( $@ ) {\r
-        croak "Error while parsing Description XML: $@";\r
-    }\r
-\r
-    return $self;\r
-}\r
-\r
-sub load {\r
-    my $self = shift;\r
-    my $xml  = shift;\r
-    \r
-    my $parser   = XML::LibXML->new;\r
-    my $doc      = $parser->parse_string( $xml );\r
-    my $element  = $doc->documentElement;\r
-    my $nodename = $element->nodeName;\r
-\r
-    croak "Node should be OpenSearchDescription: $nodename" if $nodename ne 'OpenSearchDescription';\r
-\r
-    my $ns = $element->getNamespace->value;\r
-    my $version;\r
-    if( $ns eq 'http://a9.com/-/spec/opensearch/1.1/' ) {\r
-        $self->ns( $ns );\r
-        $version = '1.1';\r
-    }\r
-    else {\r
-        $version = '1.0';\r
-    }\r
-    $self->version( $version );\r
-\r
-    for my $column ( @columns ) {\r
-        my $node = $doc->documentElement->getChildrenByTagName( $column ) or next;\r
-        if( $column eq 'Url' ) {\r
-            if( $version eq '1.0' ) {\r
-                $self->Url( [ WWW::OpenSearch::Url->new( template => $node->string_value, type => 'application/rss+xml' ) ] );\r
-                next;\r
-            }\r
-\r
-            my @url;\r
-            for my $urlnode ( $node->get_nodelist ) {\r
-                my $type = $urlnode->getAttributeNode( 'type' )->value;\r
-                my $url  = $urlnode->getAttributeNode( 'template' )->value;\r
-                $url =~ s/\?}/}/g; # optional\r
-                my $method = $urlnode->getAttributeNode( 'method' );\r
-                $method = $method->value if $method;\r
-
-                my %params;\r
-                for( $urlnode->getChildrenByTagName( 'Param' ) ) {\r
-                    my $param = $_->getAttributeNode( 'name' )->value;\r
+Return all of the urls associated with this description in an array.
+
+=head2 get_best_url( )
+
+Attempts to retrieve the best URL associated with this description, based
+on the following content types (from most preferred to least preferred):
+
+=over 4
+
+=item * application/atom+xml
+
+=item * application/rss+xml
+
+=item * text/xml
+
+=back
+
+=head2 get_url_by_type( $type )
+
+Retrieves the first WWW::OpenSearch::URL associated with this description
+whose type is equal to $type.
+
+=head1 ACCESSORS
+
+=head2 version( )
+
+=head2 ns( )
+
+=head2 AdultContent( )
+
+=head2 Contact( )
+
+=head2 Description( )
+
+=head2 Developer( )
+
+=head2 Format( )
+
+=head2 Image( )
+
+=head2 LongName( )
+
+=head2 Query( )
+
+=head2 SampleSearch( )
+
+=head2 ShortName( )
+
+=head2 SyndicationRight( )
+
+=head2 Tags( )
+
+=head2 Url( )
+
+=head1 AUTHOR
+
+=over 4
+
+=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
+
+=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
+
+=back
+
+=head1 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. 
+
+=cut
+
+for( @columns ) {
+    no strict 'refs';
+    my $col = lc;
+    *$_ = \&$col;
+}
+
+sub new {
+    my $class = shift;
+    my $xml   = shift;
+    
+    my $self  = $class->SUPER::new;
+    
+    eval{ $self->load( $xml ); } if $xml;
+    if( $@ ) {
+        croak "Error while parsing Description XML: $@";
+    }
+
+    return $self;
+}
+
+sub load {
+    my $self = shift;
+    my $xml  = shift;
+    
+    my $parser   = XML::LibXML->new;
+    my $doc      = $parser->parse_string( $xml );
+    my $element  = $doc->documentElement;
+    my $nodename = $element->nodeName;
+
+    croak "Node should be OpenSearchDescription: $nodename" if $nodename ne 'OpenSearchDescription';
+
+    my $ns = $element->getNamespace->value;
+    my $version;
+    if( $ns eq 'http://a9.com/-/spec/opensearch/1.1/' ) {
+        $self->ns( $ns );
+        $version = '1.1';
+    }
+    else {
+        $version = '1.0';
+    }
+    $self->version( $version );
+
+    for my $column ( @columns ) {
+        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' ) ] );
+                next;
+            }
+
+            my @url;
+            for my $urlnode ( $node->get_nodelist ) {
+                my $type = $urlnode->getAttributeNode( 'type' )->value;
+                my $url  = $urlnode->getAttributeNode( 'template' )->value;
+                $url =~ s/\?}/}/g; # optional
+                my $method = $urlnode->getAttributeNode( 'method' );
+                $method = $method->value if $method;
+
+                my %params;
+                for( $urlnode->getChildrenByTagName( 'Param' ) ) {
+                    my $param = $_->getAttributeNode( 'name' )->value;
                     my $value = $_->getAttributeNode( 'value' )->value;
-                    $value    =~ s/\?}/}/g; # optional\r
-                    $params{ $param } = $value;\r
-                }\r
-\r
-                push @url, WWW::OpenSearch::Url->new( template => $url, type => $type, method => $method, params => \%params );\r
-            }\r
-            $self->Url( \@url );\r
-        }\r
-        elsif( $version eq '1.1' and $column eq 'Query' ) {\r
-            my $query = ( $node->get_nodelist )[ 0 ];\r
-            next if $query->getAttributeNode( 'role' )->value eq 'example';\r
-            $self->SampleSearch( $query->getAttributeNode( 'searchTerms' )->value );\r
-        }\r
-        elsif( $version eq '1.0' and $column eq 'Format' ) {\r
-            $self->Format( $node->string_value );\r
-            $self->ns( $self->Format );\r
-        }\r
-        else {\r
-            $self->$column( $node->string_value );\r
-        }\r
-    }\r
-}\r
-\r
-sub get_best_url {\r
-    my $self = shift;\r
-    \r
-    return $self->get_url_by_type( 'application/atom+xml' )\r
-        || $self->get_url_by_type( 'application/rss+xml' )\r
-        || $self->get_url_by_type( 'text/xml' )\r
-        || $self->url->[ 0 ];\r
-}\r
-\r
-sub get_url_by_type {\r
-    my $self = shift;\r
-    my $type = shift;\r
-    \r
-    my $template;\r
-    for( $self->urls ) {\r
-        $template = $_ if $_->type eq $type;\r
-        last;\r
-    };\r
-    \r
-    return $template;\r
-}\r
+                    $value    =~ s/\?}/}/g; # optional
+                    $params{ $param } = $value;
+                }
+
+                push @url, WWW::OpenSearch::Url->new( template => $url, type => $type, method => $method, params => \%params );
+            }
+            $self->Url( \@url );
+        }
+        elsif( $version eq '1.1' and $column eq 'Query' ) {
+            my $queries = $self->query || [];
+
+            for my $node ( $node->get_nodelist ) {
+                my $query = WWW::OpenSearch::Query->new( {
+                    map { $_ => $node->getAttributeNode( $_ )->value } qw( role searchTerms )
+                } );
+
+                push @$queries, $query;
+            }
+
+            $self->query( $queries );
+        }
+        elsif( $version eq '1.1' and $column eq 'Image' ) {
+            my $images = $self->image || [];
+
+            for my $node ( $node->get_nodelist ) {
+                my $image = WWW::OpenSearch::Image->new( {
+                    ( map { my $attr = $node->getAttributeNode( $_ ); $attr ? ($_ => $attr->value) : () } qw( height width type ) ),
+                    url => $node->string_value
+                } );
+
+                push @$images, $image;
+            }
+
+            $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 );
+        }
+    }
+}
+
+sub get_best_url {
+    my $self = shift;
+    
+    return $self->get_url_by_type( 'application/atom+xml' )
+        || $self->get_url_by_type( 'application/rss+xml' )
+        || $self->get_url_by_type( 'text/xml' )
+        || $self->url->[ 0 ];
+}
+
+sub get_url_by_type {
+    my $self = shift;
+    my $type = shift;
+    
+    my $template;
+    for( $self->urls ) {
+        $template = $_ if $_->type eq $type;
+        last;
+    };
+    
+    return $template;
+}
 
 sub urls {
     my $self = shift;
     return @{ $self->url };
 }
-\r
-1;\r
+
+1;
diff --git a/lib/WWW/OpenSearch/Image.pm b/lib/WWW/OpenSearch/Image.pm
new file mode 100644 (file)
index 0000000..945b58e
--- /dev/null
@@ -0,0 +1,55 @@
+package WWW::OpenSearch::Image;
+
+use strict;
+use warnings;
+
+use base qw( Class::Accessor::Fast );
+
+__PACKAGE__->mk_accessors( qw( height width type url ) );
+
+=head1 NAME
+
+WWW::OpenSearch::Image - Object to represent an image
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 CONSTRUCTOR
+
+=head2 new( [%options] )
+
+=head1 ACCESSORS
+
+=over 4
+
+=item * height
+
+=item * width
+
+=item * type
+
+=item * url
+
+=back
+
+=head1 AUTHOR
+
+=over 4
+
+=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
+
+=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
+
+=back
+
+=head1 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. 
+
+=cut
+
+1;
diff --git a/lib/WWW/OpenSearch/Query.pm b/lib/WWW/OpenSearch/Query.pm
new file mode 100644 (file)
index 0000000..52d7eff
--- /dev/null
@@ -0,0 +1,51 @@
+package WWW::OpenSearch::Query;
+
+use strict;
+use warnings;
+
+use base qw( Class::Accessor::Fast );
+
+__PACKAGE__->mk_accessors( qw( role searchTerms ) );
+
+=head1 NAME
+
+WWW::OpenSearch::Query - Object to represent a sample query
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 CONSTRUCTOR
+
+=head2 new( [%options] )
+
+=head1 ACCESSORS
+
+=over 4
+
+=item * role
+
+=item * searchTerms
+
+=back
+
+=head1 AUTHOR
+
+=over 4
+
+=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
+
+=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
+
+=back
+
+=head1 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. 
+
+=cut
+
+1;
index 1cb653b..816476a 100644 (file)
-package WWW::OpenSearch::Response;\r
-\r
-use strict;\r
-use warnings;\r
-\r
-use base qw( HTTP::Response Class::Accessor::Fast );\r
-\r
+package WWW::OpenSearch::Response;
+
+use strict;
+use warnings;
+
+use base qw( HTTP::Response Class::Accessor::Fast );
+
 use XML::Feed;
-use URI;\r
-use Data::Page;\r
-\r
-__PACKAGE__->mk_accessors( qw( feed pager parent ) );\r
-\r
-=head1 NAME\r
-\r
-WWW::OpenSearch::Response - Encapsulate a response received from\r
-an A9 OpenSearch compatible engine\r
-\r
-=head1 SYNOPSIS\r
-    \r
-    use WWW::OpenSearch;\r
-    \r
-    my $url = "http://bulkfeeds.net/opensearch.xml";\r
-    my $engine = WWW::OpenSearch->new($url);\r
-    \r
-    # Retrieve page 4 of search results for "iPod"\r
-    my $response = $engine->search("iPod",{ startPage => 4 });\r
-    for my $item (@{$response->feed->items}) {\r
-        print $item->{description};\r
-    }\r
-    \r
-    # Retrieve page 3 of results\r
-    $response = $response->previous_page;\r
-    \r
-    # Retrieve page 5 of results\r
-    $response = $response->next_page;\r
-    \r
-=head1 DESCRIPTION\r
-\r
-WWW::OpenSearch::Response is a module designed to encapsulate a\r
-response received from an A9 OpenSearch compatible engine.\r
-See http://opensearch.a9.com/spec/1.1/response/ for details.\r
-\r
-=head1 CONSTRUCTOR\r
-\r
-=head2 new( $parent, $response )\r
-\r
-Constructs a new instance of WWW::OpenSearch::Response. Arguments\r
-include the WWW::OpenSearch object which initiated the search (parent)\r
-and the HTTP::Response returned by the search request.\r
-\r
-=head1 METHODS\r
-\r
-=head2 parse_response( )\r
-\r
-Parses the content of the HTTP response using XML::Feed. If successful,\r
-parse_feed( ) is also called.\r
-\r
-=head2 parse_feed( )\r
-\r
-Parses the XML::Feed originally parsed from the HTTP response content.\r
-Sets the pager object appropriately.\r
-\r
-=head2 previous_page( ) / next_page( )\r
-\r
-Performs another search on the parent object, returning a\r
-WWW::OpenSearch::Response instance containing the previous/next page\r
-of results. If the current response includes a &lt;link rel="previous/next"\r
-href="..." /&gt; tag, the page will simply be the parsed content of the URL\r
-specified by the tag's href attribute. However, if the current response does not\r
-include the appropriate link, a new query is constructed using the startPage\r
-or startIndex query arguments.\r
-\r
-=head2 _get_link( $type )\r
-\r
-Gets the href attribute of the first link whose rel attribute\r
-is equal to $type.\r
-\r
-=head1 ACCESSORS\r
-\r
-=head2 feed( )\r
-\r
-=head2 pager( )\r
-\r
-=head2 parent( )\r
-\r
-=head1 AUTHOR\r
-\r
-=over 4\r
-\r
-=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>\r
-\r
-=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>\r
-\r
-=back\r
-\r
-=head1 COPYRIGHT AND LICENSE\r
-\r
-Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy\r
-\r
-This library is free software; you can redistribute it and/or modify\r
-it under the same terms as Perl itself. \r
-\r
-=cut\r
-\r
-sub new {\r
-    my $class    = shift;\r
-    my $parent   = shift;\r
-    my $response = shift;\r
-    \r
-    my $self = bless $response, $class;\r
-\r
-    $self->parent( $parent );\r
-    return $self unless $self->is_success;\r
-    \r
-    $self->parse_response;\r
-    \r
-    return $self;\r
-}\r
-\r
-sub parse_response {\r
-    my $self = shift;\r
-\r
-    my $content = $self->content;\r
-    my $feed    = XML::Feed->parse( \$content );\r
-\r
-    return if XML::Feed->errstr;\r
-    $self->feed( $feed );\r
-    \r
-    $self->parse_feed;\r
-}\r
-\r
-sub parse_feed {\r
-    my $self  = shift;\r
-    my $pager = Data::Page->new;\r
-\r
-    my $feed   = $self->feed;\r
-    my $format = $feed->format;\r
-    my $ns     = $self->parent->description->ns;\r
-    \r
-    # TODO\r
-    # adapt these for any number of opensearch elements in\r
-    # the feed or in each entry\r
-    \r
-    if( my $atom = $feed->{ atom } ) {\r
-        my $total   = $atom->get( $ns, 'totalResults' );\r
-        my $perpage = $atom->get( $ns, 'itemsPerPage' );\r
-        my $start   = $atom->get( $ns, 'startIndex' );\r
-        \r
-        $pager->total_entries( $total );\r
-        $pager->entries_per_page( $perpage );\r
-        $pager->current_page( $start ? ( $start - 1 ) / $perpage + 1 : 0 )\r
-    }\r
-    elsif( my $rss = $feed->{ rss } ) {\r
-       if ( my $page = $rss->channel->{ $ns } ) {\r
-            $pager->total_entries(    $page->{ totalResults } );\r
-            $pager->entries_per_page( $page->{ itemsPerPage } );\r
-            my $start = $page->{ startIndex };\r
-            $pager->current_page( $start ? ( $start - 1 ) / $page->{ itemsPerPage } + 1 : 0 )\r
-        }\r
-    }    \r
-    $self->pager( $pager );\r
-}\r
-\r
-sub next_page {\r
-    my $self  = shift;\r
-    return $self->_get_page( 'next' );\r
-}\r
-\r
-sub previous_page {\r
+use URI;
+use Data::Page;
+
+__PACKAGE__->mk_accessors( qw( feed pager parent ) );
+
+=head1 NAME
+
+WWW::OpenSearch::Response - Encapsulate a response received from
+an A9 OpenSearch compatible engine
+
+=head1 SYNOPSIS
+    
+    use WWW::OpenSearch;
+    
+    my $url = "http://bulkfeeds.net/opensearch.xml";
+    my $engine = WWW::OpenSearch->new($url);
+    
+    # Retrieve page 4 of search results for "iPod"
+    my $response = $engine->search("iPod",{ startPage => 4 });
+    for my $item (@{$response->feed->items}) {
+        print $item->{description};
+    }
+    
+    # Retrieve page 3 of results
+    $response = $response->previous_page;
+    
+    # Retrieve page 5 of results
+    $response = $response->next_page;
+    
+=head1 DESCRIPTION
+
+WWW::OpenSearch::Response is a module designed to encapsulate a
+response received from an A9 OpenSearch compatible engine.
+See http://opensearch.a9.com/spec/1.1/response/ for details.
+
+=head1 CONSTRUCTOR
+
+=head2 new( $parent, $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.
+
+=head1 METHODS
+
+=head2 parse_response( )
+
+Parses the content of the HTTP response using XML::Feed. If successful,
+parse_feed( ) is also called.
+
+=head2 parse_feed( )
+
+Parses the XML::Feed originally parsed from the HTTP response content.
+Sets the pager object appropriately.
+
+=head2 previous_page( ) / next_page( )
+
+Performs another search on the parent object, returning a
+WWW::OpenSearch::Response instance containing the previous/next page
+of results. If the current response includes a &lt;link rel="previous/next"
+href="..." /&gt; tag, the page will simply be the parsed content of the URL
+specified by the tag's href attribute. However, if the current response does not
+include the appropriate link, a new query is constructed using the startPage
+or startIndex query arguments.
+
+=head2 _get_link( $type )
+
+Gets the href attribute of the first link whose rel attribute
+is equal to $type.
+
+=head1 ACCESSORS
+
+=head2 feed( )
+
+=head2 pager( )
+
+=head2 parent( )
+
+=head1 AUTHOR
+
+=over 4
+
+=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
+
+=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
+
+=back
+
+=head1 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. 
+
+=cut
+
+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;
+    
+    return $self;
+}
+
+sub parse_response {
+    my $self = shift;
+
+    my $content = $self->content;
+    my $feed    = XML::Feed->parse( \$content );
+
+    return if XML::Feed->errstr;
+    $self->feed( $feed );
+    
+    $self->parse_feed;
+}
+
+sub parse_feed {
+    my $self  = shift;
+    my $pager = Data::Page->new;
+
+    my $feed   = $self->feed;
+    my $format = $feed->format;
+    my $ns     = $self->parent->description->ns;
+    
+    # TODO
+    # adapt these for any number of opensearch elements in
+    # the feed or in each entry
+    
+    if( my $atom = $feed->{ atom } ) {
+        my $total   = $atom->get( $ns, 'totalResults' );
+        my $perpage = $atom->get( $ns, 'itemsPerPage' );
+        my $start   = $atom->get( $ns, 'startIndex' );
+        
+        $pager->total_entries( $total );
+        $pager->entries_per_page( $perpage );
+        $pager->current_page( $start ? ( $start - 1 ) / $perpage + 1 : 0 )
+    }
+    elsif( my $rss = $feed->{ rss } ) {
+       if ( my $page = $rss->channel->{ $ns } ) {
+            $pager->total_entries(    $page->{ totalResults } );
+            $pager->entries_per_page( $page->{ itemsPerPage } );
+            my $start = $page->{ startIndex };
+            $pager->current_page( $start ? ( $start - 1 ) / $page->{ itemsPerPage } + 1 : 0 )
+        }
+    }    
+    $self->pager( $pager );
+}
+
+sub next_page {
+    my $self  = shift;
+    return $self->_get_page( 'next' );
+}
+
+sub previous_page {
     my $self  = shift;
-    return $self->_get_page( 'previous' );\r
+    return $self->_get_page( 'previous' );
 }
 
 sub _get_page {
     my( $self, $direction ) = @_;    
     my $pager       = $self->pager;
-    my $pagermethod = "${direction}_page";\r
-    my $page        = $pager->$pagermethod;\r
-    return unless $page;\r
+    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\r
-        my $link = $self->_get_link( $direction );\r
+    if( $method ne 'post' ) { # force query build on POST
+        my $link = $self->_get_link( $direction );
         return $self->parent->do_search( $link, $method ) if $link;
-    }\r
-    \r
+    }
+    
     my $template = $self->parent->description->get_best_url;
     my( $param, $query );
     if( $method eq 'post' ) {
@@ -195,11 +195,11 @@ sub _get_page {
     }
     else {
         $query = { $self->request->uri->query_form };
-    }\r
-\r
-    if( $param = $template->macros->{ startPage } ) {\r
-        $query->{ $param } = $pager->$pagermethod\r
-    }\r
+    }
+
+    if( $param = $template->macros->{ startPage } ) {
+        $query->{ $param } = $pager->$pagermethod
+    }
     elsif( $param = $template->macros->{ startIndex } ) {
         if( $query->{ $param } ) {
             $query->{ $param } = $direction eq 'previous'
@@ -210,24 +210,24 @@ sub _get_page {
             $query->{ $param } = $direction eq 'previous'
                 ? 1
                 : $pager->entries_per_page + 1;
-        }\r
-    }\r
-\r
-    return $self->parent->do_search( $template->prepare_query( $query ), $method );\r
-}\r
-\r
-sub _get_link {\r
-    my $self = shift;\r
-    my $type = shift;\r
-    my $feed = $self->feed->{ atom };\r
-    \r
-    return unless $feed;\r
-    \r
-    for( $feed->link ) {\r
-        return $_->get( 'href' ) if $_->get( 'rel' ) eq $type;\r
+        }
     }
 
-    return;\r
-}\r
-\r
-1;\r
+    return $self->parent->do_search( $template->prepare_query( $query ), $method );
+}
+
+sub _get_link {
+    my $self = shift;
+    my $type = shift;
+    my $feed = $self->feed->{ atom };
+    
+    return unless $feed;
+    
+    for( $feed->link ) {
+        return $_->get( 'href' ) if $_->get( 'rel' ) eq $type;
+    }
+
+    return;
+}
+
+1;
index 33cbd0a..5303930 100644 (file)
-package WWW::OpenSearch::Url;\r
-\r
-use base qw( Class::Accessor::Fast );\r
-\r
-use URI;\r
-use URI::Escape;\r
-\r
-__PACKAGE__->mk_accessors( qw( type template method params macros ) );\r
-\r
-=head1 NAME\r
-\r
-WWW::OpenSearch::Url\r
-\r
-=head1 SYNOPSIS\r
-\r
-=head1 DESCRIPTION\r
-\r
-=head1 CONSTRUCTOR\r
-\r
-=head2 new( [%options] )\r
-\r
-=head1 METHODS\r
-\r
-=head2 parse_macros( )\r
-\r
-=head2 prepare_query( [ \%params ] )\r
-\r
-=head1 ACCESSORS\r
-\r
-=head1 AUTHOR\r
-\r
-=over 4\r
-\r
-=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>\r
-\r
-=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>\r
-\r
-=back\r
-\r
-=head1 COPYRIGHT AND LICENSE\r
-\r
-Copyright 2006 by Tatsuhiko Miyagawa and Brian Cassidy\r
-\r
-This library is free software; you can redistribute it and/or modify\r
-it under the same terms as Perl itself. \r
-\r
-=cut\r
-\r
-sub new {\r
-    my( $class, %options ) = @_;\r
-    \r
-    $options{ method } ||= 'GET';\r
-    $options{ template } = URI->new( $options{ template } );\r
-    \r
+package WWW::OpenSearch::Url;
+
+use strict;
+use warnings;
+
+use base qw( Class::Accessor::Fast );
+
+use URI;
+use URI::Escape;
+
+__PACKAGE__->mk_accessors( qw( type template method params macros ) );
+
+=head1 NAME
+
+WWW::OpenSearch::Url - Object to represent a target URL
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 CONSTRUCTOR
+
+=head2 new( [%options] )
+
+=head1 METHODS
+
+=head2 parse_macros( )
+
+=head2 prepare_query( [ \%params ] )
+
+=head1 ACCESSORS
+
+=over 4
+
+=item * type
+
+=item * template
+
+=item * method
+
+=item * params
+
+=item * macros
+
+=back
+
+=head1 AUTHOR
+
+=over 4
+
+=item * Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
+
+=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
+
+=back
+
+=head1 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. 
+
+=cut
+
+sub new {
+    my( $class, %options ) = @_;
+    
+    $options{ method } ||= 'GET';
+    $options{ template } = URI->new( $options{ template } );
+    
     my $self = $class->SUPER::new( \%options );
-    $self->parse_macros;\r
-
-    return $self;\r
-}\r
-\r
-sub parse_macros {\r
-    my $self = shift;\r
-    \r
+    $self->parse_macros;
+
+    return $self;
+}
+
+sub parse_macros {
+    my $self = shift;
+    
     my %query = $self->method eq 'post'
         ? %{ $self->params }
-        : $self->template->query_form;\r
-    \r
-    my %macros;\r
-    for( keys %query ) {\r
-        if( $query{ $_ } =~ /^{(.+)}$/ ) {\r
-            $macros{ $1 } = $_;\r
-        }\r
-    }\r
-    \r
-    $self->macros( \%macros );\r
-}\r
-\r
-sub prepare_query {\r
-    my( $self, $params ) = @_;\r
-    my $url   = $self->template->clone;\r
-    \r
-    $params->{ startIndex     } ||= 1;\r
-    $params->{ startPage      } ||= 1;\r
-    $params->{ language       } ||= '*';\r
-    $params->{ outputEncoding } ||= 'UTF-8';\r
-    $params->{ inputEncoding  } ||= 'UTF-8';\r
-    \r
-    my $macros = $self->macros;\r
+        : $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;
+    
+    $params->{ startIndex     } ||= 1;
+    $params->{ startPage      } ||= 1;
+    $params->{ language       } ||= '*';
+    $params->{ outputEncoding } ||= 'UTF-8';
+    $params->{ inputEncoding  } ||= 'UTF-8';
+    
+    my $macros = $self->macros;
 
     # attempt to handle POST
     if( $self->method eq 'post' ) {
         my $post = $self->params;
-        for( keys %macros ) {
-            $post->{ $macros->{ $_ } } = $params->{ $_ };\r
+        for( keys %$macros ) {
+            $post->{ $macros->{ $_ } } = $params->{ $_ };
         }
         return [ $url, $post ];
     }
 
-    my $query = { $url->query_form };\r
-    for( keys %$macros ) {\r
-        $query->{ $macros->{ $_ } } = $params->{ $_ };\r
-    }\r
-    \r
-    $url->query_form( $query );\r
-    return $url;\r
-}\r
-\r
-1;\r
+    my $query = { $url->query_form };
+    for( keys %$macros ) {
+        $query->{ $macros->{ $_ } } = $params->{ $_ };
+    }
+    
+    $url->query_form( $query );
+    return $url;
+}
+
+1;
index 208a797..32a1ed5 100644 (file)
@@ -1,9 +1,11 @@
 use strict;
-use Test::More tests => 4;
+use Test::More tests => 6;
 
 BEGIN {
     use_ok 'WWW::OpenSearch';
     use_ok 'WWW::OpenSearch::Description';
     use_ok 'WWW::OpenSearch::Response';
     use_ok 'WWW::OpenSearch::Url';
+    use_ok 'WWW::OpenSearch::Query';
+    use_ok 'WWW::OpenSearch::Image';
 }
index 4270b3b..4e23a8c 100644 (file)
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 38;
+use Test::More tests => 48;
 
 use_ok( 'WWW::OpenSearch::Description' );
 
@@ -27,7 +27,7 @@ use_ok( 'WWW::OpenSearch::Description' );
     is( $osd->contact, 'admin@example.com' );
 
     # count the urls
-    is( $osd->urls, 1 );
+    is( $osd->urls, 1, 'number of url objects' );
 }
 
 # complex 1.1 OSD
@@ -83,15 +83,24 @@ use_ok( 'WWW::OpenSearch::Description' );
     is( $osd->adultcontent, 'false' );
     is( $osd->syndicationright, 'open' );
 
-    is( $osd->image, 'http://example.com/websearch.png' );
-    
-SKIP: {
-        skip 'Test Query', 1;
-        is( $osd->query, undef );
-    };
+    my $queries = $osd->query;
+    is( scalar @$queries, 1, 'number of query objects' );
+    is( $queries->[ 0 ]->role, 'example' );
+    is( $queries->[ 0 ]->searchTerms, 'cat' );
+
+    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' );
 
     # count the urls
-    is( $osd->urls, 3 );
+    is( $osd->urls, 3, 'number of url objects' );
 }
 
 # 1.0 OSD