| XML-OverHTTP documentation | Contained in the XML-OverHTTP distribution. |
XML::OverHTTP - A base class for XML over HTTP-styled web service interface
This module is not used directly from end-users. As a child class of this, module authors can easily write own interface module for XML over HTTP-styled web service.
This module provides some methods and requires other methods overridden by child classes. The following methods are to be called in your module or by its users.
This constructor method returns a new object for your users. It accepts query parameters by hash.
my $api = MyAPI->new( %param );
MyAPI.pm inherits this XML::OverHTTP modules.
This method adds query parameters for the request.
$api->add_param( %param );
It does not validate key names.
This method returns a current query parameter.
$api->get_param( 'key' );
This method returns an XML::TreePP object to make the request.
$api->treepp->get( 'key' );
And you can set its object as well.
my $mytpp = XML::TreePP->new;
$api->treepp( $mytpp );
total_entries, entries_per_page and current_page parameters
in $mytpp are updated.
This method makes the request for the web service and returns its response tree.
my $tree = $api->request;
After calling this method, the following methods are available.
This method returns the whole of the response parsed by XML::TreePP parser.
my $tree = $api->tree;
Every element is blessed when elem_class is defined.
This method returns the root element in the response.
my $root = $api->root;
This method returns the response context itself.
print $api->xml, "\n";
This method returns the response status code.
my $code = $api->code; # usually "200" when succeeded
This method returns a Data::Page object to create page navigation.
my $pager = $api->page;
print "Last page: ", $pager->last_page, "\n";
And you can set its object as well.
my $pager = Data::Page->new;
$api->page( $pager );
This method returns a Data::Pageset object to create page navigation.
The paging mode is fixed as default.
my $pager = $api->pageset;
$pager->pages_per_set( 10 );
print "First page of next page set: ", $page_info->next_set, "\n";
Or set it to slide mode if you want.
my $pager = $api->pageset( 'slide' );
This method returns pair(s) of query key and value to set the page number for the next request.
my $hash = $api->page_param( $page );
The optional second argument specifies the number of entries per page.
my $hash = $api->page_param( $page, $size );
The optional third argument incluedes some other query parameters.
my $newhash = $api->page_param( $page, $size, $oldhash );
This method returns a processed query string which is joined by '&' delimiter.
my $query = $api->page_query(); # current page
my $query = $api->page_query( $page, $size, $hash ); # specified page
You MUST override at least one method below:
This is a method to specify the url for the request to the web service. E.g.,
sub url { 'http://www.example.com/api/V1/' }
The methods that you SHOULD override in your module are below:
This is a method to specify a root element name in the response. E.g.,
sub root_elem { 'rdf:RDF' }
This is a method to return true value when the response seems
to have error. This returns undef when it succeeds.
E.g.,
sub is_error { $_[0]->root->{status} != 'OK' }
This is a method to return the number of total entries for Data::Page.
E.g.,
sub total_entries { $_[0]->root->{hits} }
This is a method to return the number of entries per page for Data::Page.
E.g.,
sub entries_per_page { $_[0]->root->{-count} }
This is a method to return the current page number for Data::Page.
E.g.,
sub current_page { $_[0]->root->{-page} }
This is a method to return paging parameters for the next request. E.g.,
sub page_param {
my $self = shift;
my $page = shift || $self->current_page();
my $size = shift || $self->entries_per_page();
my $hash = shift || {};
$hash->{page} = $page if defined $page;
$hash->{count} = $size if defined $size;
$hash;
}
When your API uses SQL-like query parameters, offset and limit:
sub page_param {
my $self = shift;
my $page = shift || $self->current_page() or return;
my $size = shift || $self->entries_per_page() or return;
my $hash = shift || {};
$hash->{offset} = ($page-1) * $size;
$hash->{limit} = $size;
$hash;
}
You CAN override the following methods as well.
This is a method to specify the HTTP method, 'GET' or 'POST', for the request. This returns 'GET' as default. E.g.,
sub http_method { 'GET' }
This is a method to specify pairs of default query parameter and its value for the request. E.g.,
sub default_param { { method => 'search', lang => 'perl' } }
This is a method to specify a list of query parameters which are required by the web service. E.g.,
sub notnull_param { [qw( api_key secret query )] }
These keys are checked before makeing a request for safe.
This is a method to specify a class name for query parameters. E.g.,
sub elem_class { 'MyAPI::Query' }
The default value is undef, it means
a normal hash is used instead.
This is a method to specify a prefix for each attribute in the response tree. XML::TreePP uses it. E.g.,
sub attr_prefix { '' }
The default prefix is zero-length string "" which is recommended.
This is a method to specify a hash key for text nodes in the response tree. XML::TreePP uses it. E.g.,
sub text_node_key { '_text' }
The default key is "#text".
This is a method to specify a base class name for each element in the response tree. XML::TreePP uses it. E.g.,
sub elem_class { 'MyAPI::Element' }
The default value is undef, it means
each elements is a just hashref and not bless-ed.
This is a method to specify a list of element names which should always be forced into an array representation in the response tree. XML::TreePP uses it. E.g.,
sub force_array { [qw( rdf:li item xmlns )] }
This is a method to specify a list of element names which should always be forced into an hash representation in the response tree. XML::TreePP uses it. E.g.,
sub force_hash { [qw( item image )] }
Yusuke Kawasaki http://www.kawa.net/
Copyright (c) 2007 Yusuke Kawasaki. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| XML-OverHTTP documentation | Contained in the XML-OverHTTP distribution. |
package XML::OverHTTP; use strict; use vars qw( $VERSION @ISA ); $VERSION = '0.08'; use XML::TreePP; use CGI; # use Data::Page; # use Data::Pageset; use base qw( Class::Accessor::Fast ); __PACKAGE__->mk_accessors(qw( xml tree code param )); if ( $XML::TreePP::VERSION < 0.26 ) { Carp::croak( 'XML::TreePP version 0.26 or later is required' ); } package XML::OverHTTP::Default; use strict; use vars qw( $VERSION ); $VERSION = $XML::OverHTTP::VERSION; sub http_method { 'GET'; } sub url { undef; } sub query_class { undef; } sub default_param { {}; } sub notnull_param { []; } sub force_array { []; } sub force_hash { []; } sub attr_prefix { ''; } sub text_node_key { '#text'; } sub elem_class { undef; } sub root_elem { undef; } sub is_error { undef; } sub total_entries { undef; } sub entries_per_page { undef; } sub current_page { undef; } sub page_param { undef; } package XML::OverHTTP; # again use strict; use base qw( XML::OverHTTP::Default ); sub new { my $package = shift; my $self = {}; bless $self, $package; my $default = $self->default_param(); $self->add_param( %$default ) if ref $default; $self->add_param( @_ ) if scalar @_; $self; } sub new_param { my $self = shift; my $class = $self->query_class(); return {} unless defined $class; $class->new(); } sub add_param { my $self = shift; my $param = $self->param() || $self->new_param(); %$param = ( %$param, @_ ) if scalar @_; $self->param( $param ); } sub get_param { my $self = shift; my $key = shift; my $param = $self->param() or return; $param->{$key} if exists $param->{$key}; } sub treepp { my $self = shift; $self->{treepp} = shift if scalar @_; return $self->{treepp} if ref $self->{treepp}; $self->{treepp} = XML::TreePP->new(); } sub init_treepp { my $self = shift; my $treepp = $self->treepp(); my $force_array = $self->force_array(); my $force_hash = $self->force_hash(); my $attr_prefix = $self->attr_prefix(); my $text_node_key = $self->text_node_key(); # my $base_class = $self->base_class(); my $elem_class = $self->elem_class(); $treepp->set( force_array => $force_array ); $treepp->set( force_hash => $force_hash ); $treepp->set( attr_prefix => $attr_prefix ); $treepp->set( text_node_key => $text_node_key ); # $treepp->set( base_class => $base_class ); $treepp->set( elem_class => $elem_class ); $treepp; } sub request { my $self = shift; $self->{tree} = undef; $self->{xml} = undef; $self->{code} = undef; $self->{page} = undef; $self->{pageset} = undef; $self->check_param(); my $req = $self->http_request(); my $treepp = $self->init_treepp(); my( $tree, $xml, $code ) = $treepp->parsehttp( @$req ); $self->{tree} = $tree; $self->{xml} = $xml; $self->{code} = $code; $tree; } sub http_request { my $self = shift; my $method = $self->http_method(); my $url = $self->url(); my $query = $self->query_string(); Carp::croak( 'HTTP method is not defined' ) unless defined $method; Carp::croak( 'Request url is not defined' ) unless defined $url; my $req; if ( uc($method) eq 'GET' ) { $url .= '?'.$query if length($query); $req = [ $method, $url ]; } else { $req = [ $method, $url, $query ]; } $req; } sub root { my $self = shift; my $tree = $self->tree(); Carp::croak( 'Empty response' ) unless ref $tree; my $root = $self->root_elem(); Carp::croak( 'Root element is not defined' ) unless defined $root; Carp::croak( 'Root element seems empty' ) unless ref $tree->{$root}; $tree->{$root}; } sub root_elem { my $self = shift; my $tree = $self->tree(); Carp::croak( 'Empty response' ) unless ref $tree; Carp::croak( 'Multiple root elements found' ) if ( scalar keys %$tree > 1 ); # root element auto discovery by default ( keys %$tree )[0]; } sub query_string { my $self = shift; my $param = $self->param() or return; local $CGI::USE_PARAM_SEMICOLONS = 0; my $hash = { %$param }; # copy for blessed hash CGI->new( $hash )->query_string(); } sub check_param { my $self = shift; my $param = $self->param() or return; my $check = $self->notnull_param() or return; my $error = [ grep { ! exists $param->{$_} || ! defined $param->{$_} || $param->{$_} eq '' } @$check ]; return unless scalar @$error; my $join = join( ' ' => @$error ); Carp::croak "Invalid request: empty parameters - $join\n"; } sub page { my $self = shift; my $page = shift; if ( ! defined $page ) { return $self->{page} if ref $self->{page}; local $@; eval { require Data::Page; } unless $Data::Page::VERSION; Carp::croak( "Data::Page is required: $@" ) unless $Data::Page::VERSION; $page = Data::Page->new(); } my $total_entries = $self->total_entries(); my $entries_per_page = $self->entries_per_page(); my $current_page = $self->current_page(); $page->total_entries( $total_entries ); $page->entries_per_page( $entries_per_page ); $page->current_page( $current_page ); $self->{page} = $page; } sub pageset { my $self = shift; my $mode = shift; # default 'fixed', or 'slide' return $self->{pageset} if ref $self->{pageset}; my $total_entries = $self->total_entries(); my $entries_per_page = $self->entries_per_page(); my $current_page = $self->current_page(); my $hash = { total_entries => $total_entries, entries_per_page => $entries_per_page, current_page => $current_page, mode => $mode, }; local $@; eval { require Data::Pageset; } unless $Data::Pageset::VERSION; Carp::croak( "Data::Pageset is required: $@" ) unless $Data::Pageset::VERSION; $self->{pageset} = Data::Pageset->new( $hash ); } sub page_query { my $self = shift; my $param = $self->page_param( @_ ); local $CGI::USE_PARAM_SEMICOLONS = 0; CGI->new( $param )->query_string(); }
1;