view release on metacpan or search on metacpan
2007-09-12 Takeru INOUE <takeru.inoue _ gmail.com>
* release 0.1.1:
* Atompub/DateTime.pm
- rewrite POD
2007-09-10 Takeru INOUE <takeru.inoue _ gmail.com>
* release 0.1.0:
* Atompub.pm
- add some methods to HTTP::Headers, HTTP::Request, and HTTP::Response
* Atompub/Client.pm
- merge operations for entry and media resources internally
- support cache mechanizem using Last-Modified header
- refactoring drastically
* Atompub/DateTime.pm, Atompub/MediaType.pm, Atompub/Util.pm
- newly added
2007-08-13 Takeru INOUE <takeru.inoue _ gmail.com>
* release 0.0.2:
requires:
Class::Accessor::Fast: 0
Class::Data::Inheritable: 0
DateTime: 0
DateTime::Format::W3CDTF: 0
DateTime::TimeZone: 0
Digest::SHA: 0
File::Slurp: 0
HTTP::Date: 0
HTTP::Headers: 0
HTTP::Request: 0
HTTP::Response: 0
HTTP::Status: 0
MIME::Base64: 0
MIME::Types: 0
Module::Build::Compat: 0
Perl6::Export::Attrs: 0
Test::Perl::Critic: 0
Time::Local: 0
URI::Escape: 0
XML::Atom::Service: 0.016
Makefile.PL view on Meta::CPAN
requires('Class::Accessor::Fast');
requires('Class::Data::Inheritable');
requires('DateTime');
requires('DateTime::Format::W3CDTF');
requires('DateTime::TimeZone');
requires('Digest::SHA');
requires('File::Slurp');
requires('HTTP::Date');
requires('HTTP::Headers');
requires('HTTP::Request');
requires('HTTP::Response');
requires('HTTP::Status');
requires('MIME::Base64');
requires('MIME::Types');
requires('Module::Build::Compat');
requires('Perl6::Export::Attrs');
requires('Test::Perl::Critic');
requires('Time::Local');
requires('URI::Escape');
requires('XML::Atom::Service', 0.016);
lib/Atompub.pm view on Meta::CPAN
package Atompub;
use warnings;
use strict;
use 5.006;
use version 0.74; our $VERSION = qv('0.3.7');
use HTTP::Headers;
use HTTP::Request;
use HTTP::Response;
use XML::Atom;
use XML::Atom::Service 0.15.4;
our %REQUEST_HEADERS = (
accept => 'Accept',
if_match => 'If-Match',
if_none_match => 'If-None-Match',
if_modified_since => 'If-Modified-Since',
if_unmodified_since => 'If-Unmodified-Since',
lib/Atompub.pm view on Meta::CPAN
our %ENTITY_HEADERS = (
last_modified => 'Last-Modified',
slug => 'Slug',
);
while (my($method, $header) = each %REQUEST_HEADERS) {
no strict 'refs'; ## no critic
*{"HTTP::Headers::$method"} = sub { shift->header($header, @_) }
unless HTTP::Headers->can($method);
*{"HTTP::Request::$method"} = sub { shift->header($header, @_)}
unless (HTTP::Request->can($method));
}
while (my($method, $header) = each %RESPONSE_HEADERS) {
no strict 'refs'; ## no critic
*{"HTTP::Headers::$method"} = sub { shift->header($header, @_) }
unless HTTP::Headers->can($method);
*{"HTTP::Response::$method"} = sub { shift->header($header, @_) }
unless HTTP::Response->can($method);
}
while (my($method, $header) = each %ENTITY_HEADERS) {
no strict 'refs'; ## no critic
*{"HTTP::Headers::$method"} = sub { shift->header($header, @_) }
unless HTTP::Headers->can($method);
*{"HTTP::Request::$method"} = sub { shift->header($header, @_) }
unless HTTP::Request->can($method);
*{"HTTP::Response::$method"} = sub { shift->header($header, @_) }
unless HTTP::Response->can($method);
}
1; # Magic true value required at end of module
__END__
=head1 NAME
Atompub - Atom Publishing Protocol implementation
lib/Atompub.pm view on Meta::CPAN
XML formats used in the protocol are implemented in L<XML::Atom> and
L<XML::Atom::Service>.
Catalyst extension L<Catalyst::Controller::Atompub> is also available.
This module was tested in July2007InteropTokyo and November2007Interop,
and interoperated with other implementations.
See L<http://intertwingly.net/wiki/pie/July2007InteropTokyo> and
L<http://www.intertwingly.net/wiki/pie/November2007Interop> in detail.
=head1 METHODS of HTTP::Headers, HTTP::Request, and HTTP::Response
Some accessors for the HTTP header fields, which are used in the Atom Publishing Protocol,
are imported into L<HTTP::Headers>, L<HTTP::Request>, and L<HTTP::Response>.
See L<http://www.ietf.org/rfc/rfc2616.txt> in detail.
=head2 $headers->accept([ $value ])
An accessor for the I<Accept> header field.
This method is imported into L<HTTP::Headers> and L<HTTP::Request>.
=head2 $headers->if_match([ $value ])
An accessor for the I<If-Match> header field.
This method is imported into L<HTTP::Headers> and L<HTTP::Request>.
=head2 $headers->if_none_match([ $value ])
An accessor for the I<If-None-Match> header field.
This method is imported into L<HTTP::Headers> and L<HTTP::Request>.
=head2 $headers->if_modified_since([ $value ])
An accessor for the I<If-Modified-Since> header field.
$value MUST be UTC epoch value, like C<1167609600>.
This method is imported into L<HTTP::Headers> and L<HTTP::Request>.
=head2 $headers->if_unmodified_since([ $value ])
An accessor for the I<If-Unmodified-Since> header field.
$value MUST be UTC epoch value, like C<1167609600>.
This method is imported into L<HTTP::Headers> and L<HTTP::Request>.
=head2 $headers->content_location([ $value ])
An accessor for the I<Content-Location> header field.
This method is imported into L<HTTP::Headers> and L<HTTP::Response>.
=head2 $headers->etag([ $value ])
An accessor for the I<ETag> header field.
lib/Atompub.pm view on Meta::CPAN
=head2 $headers->location([ $value ])
An accessor for the I<Location> header field.
This method is imported into L<HTTP::Headers> and L<HTTP::Response>.
=head2 $headers->last_modified([ $value ])
An accessor for the I<Last-Modified> header field.
This method is imported into L<HTTP::Headers>, L<HTTP::Request>, and L<HTTP::Response>.
=head2 $headers->slug([ $value ])
An accessor for the I<Slug> header field.
This method is imported into L<HTTP::Headers>, L<HTTP::Request>, and L<HTTP::Response>.
=head1 AUTHOR
Takeru INOUE, E<lt>takeru.inoue _ gmail.comE<gt>
=head1 LICENCE AND COPYRIGHT
Copyright (c) 2007, Takeru INOUE C<< <takeru.inoue _ gmail.com> >>.
lib/Atompub/Client.pm view on Meta::CPAN
$client->_delete_resource({ uri => $uri });
}
*deleteMedia = \&deleteEntry;
sub _get_service {
my($client, $args) = @_;
my $uri = $args->{uri};
$client->_clear;
return $client->error('No URI') unless $uri;
$client->req(HTTP::Request->new(GET => $uri));
$client->res($client->make_request($client->req));
return $client->error(join "\n", $client->res->status_line, $client->res->content)
unless is_success $client->res->code;
warn 'Bad Content-Type: '.$client->res->content_type
unless media_type($client->res->content_type)->is_a('service');
$client->rc(XML::Atom::Service->new(\$client->res->content))
or return $client->error(XML::Atom::Service->errstr);
for my $work ($client->rc->workspaces) {
$client->info->put($_->href, $_) for $work->collections;
}
$client;
}
sub _get_categories {
my($client, $args) = @_;
my $uri = $args->{uri};
$client->_clear;
return $client->error('No URI') unless $uri;
$client->req(HTTP::Request->new(GET => $uri));
$client->res($client->make_request($client->req));
return $client->error(join "\n", $client->res->status_line, $client->res->content)
unless is_success $client->res->code;
warn 'Bad Content-Type: '.$client->res->content_type
unless media_type($client->res->content_type)->is_a('categories');
$client->rc(XML::Atom::Categories->new(\$client->res->content))
or return $client->error(XML::Atom::Categories->errstr);
$client;
}
sub _get_feed {
my($client, $args) = @_;
my $uri = $args->{uri};
$client->_clear;
return $client->error('No URI') unless $uri;
$client->req(HTTP::Request->new(GET => $uri));
$client->res($client->make_request($client->req));
return $client->error(join "\n", $client->res->status_line, $client->res->content)
unless is_success $client->res->code;
warn 'Bad Content-Type: '.$client->res->content_type
unless media_type($client->res->content_type)->is_a('feed');
$client->rc(XML::Atom::Feed->new(\$client->res->content))
or return $client->error(XML::Atom::Feed->errstr);
$client;
}
lib/Atompub/Client.pm view on Meta::CPAN
unless is_allowed_category($info, $entry->category);
$content = $entry->as_xml;
XML::Atom::Client::_utf8_off($content);
$headers->content_type(media_type('entry'));
$headers->content_length(length $content);
}
elsif (UNIVERSAL::isa($rc, 'SCALAR')) {
$content = $$rc;
}
$client->req(HTTP::Request->new(POST => $uri, $headers, $content));
$client->res($client->make_request($client->req));
return $client->error(join "\n", $client->res->status_line, $client->res->content)
unless is_success $client->res->code;
warn 'Bad status code: '.$client->res->code
unless $client->res->code == RC_CREATED;
return $client->error('No Locaiton') unless $client->res->location;
lib/Atompub/Client.pm view on Meta::CPAN
my $headers = HTTP::Headers->new;
my $cache = $client->cache->get($uri);
if ($cache) {
$headers->if_modified_since(datetime($cache->last_modified)->epoch)
if $cache->last_modified;
$headers->if_none_match($cache->etag) if defined $cache->etag;
}
$client->req(HTTP::Request->new(GET => $uri, $headers));
$client->res($client->make_request($client->req));
if (is_success $client->res->code) {
if (media_type($client->res->content_type)->is_a('entry')) {
$client->rc(XML::Atom::Entry->new(\$client->res->content))
or return $client->error(XML::Atom::Entry->errstr);
}
else {
$client->rc($client->res->content);
lib/Atompub/Client.pm view on Meta::CPAN
elsif (UNIVERSAL::isa($rc, 'SCALAR')) {
$content = $$rc;
}
if (my $cache = $client->cache->get($uri)) {
$headers->if_unmodified_since(datetime($cache->last_modified)->epoch)
if $cache->last_modified;
$headers->if_match($cache->etag) if defined $cache->etag;
}
$client->req(HTTP::Request->new(PUT => $uri, $headers, $content));
$client->res($client->make_request($client->req));
return $client->error(join "\n", $client->res->status_line, $client->res->content)
unless is_success $client->res->code;
return $client unless $client->res->content;
if (media_type($client->res->content_type)->is_a('entry')) {
$client->rc(XML::Atom::Entry->new(\$client->res->content))
or return $client->error(XML::Atom::Entry->errstr);
lib/Atompub/Client.pm view on Meta::CPAN
my $headers = HTTP::Headers->new;
# If-Match nor If-Unmodified-Since header is not required on DELETE
# if (my $cache = $client->cache->get($uri)) {
# $headers->if_unmodified_since(datetime($cache->last_modified)->epoch)
# if $cache->last_modified;
# $headers->if_match($cache->etag) if defined $cache->etag;
# }
$client->req(HTTP::Request->new(DELETE => $uri, $headers));
$client->res($client->make_request($client->req));
return $client->error(join "\n", $client->res->status_line, $client->res->content)
unless is_success $client->res->code;
$client;
}
sub _clear {
my($client) = @_;
lib/Atompub/Client.pm view on Meta::CPAN
# Get a href attribute of an "edit-media" link
my $edit_media_uri = $client->resource->edit_media_link;
my $binary = $client->getMedia($edit_media_uri);
$client->updateMedia($edit_media_uri, 'sample2.png', 'image/png');
$client->deleteEntry($edit_media_uri);
# Access to the requested HTTP::Request object
my $request = $client->request;
# Access to the received HTTP::Response object
my $response = $client->response;
# Access to the received resource (XML::Atom object or binary data)
my $resource = $client->resource;
=head1 DESCRIPTION
lib/Atompub/Client.pm view on Meta::CPAN
=head2 $client->rc
An accessor for Entry or Media Resource, which was retrieved in the previous action.
=head2 $client->request
=head2 $client->req
An accessor for an L<HTTP::Request> object, which was used in the previous action.
=head2 $client->response
=head2 $client->res
An accessor for an L<HTTP::Response> object, which was used in the previous action.
=head1 INTERNAL INTERFACES
t/10.client.t view on Meta::CPAN
diag "using HTTP proxy: $proxy";
$client->proxy( $proxy ) if $proxy;
}
# Service
ok !$client->getService('http://example.com/service'); # Not Found
like $client->errstr, qr/not found/i;
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
ok !$client->rc;
is $client->res->code, RC_NOT_FOUND;
ok $client->getService($SERVICE);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
isa_ok $client->rc, 'XML::Atom::Service';
ok $client->res->is_success;
my $serv = $client->rc;
my($entry_coll, $media_coll) = $serv->workspace->collections;
isa_ok $client->info->get($entry_coll->href), 'XML::Atom::Collection';
isa_ok $client->info->get($media_coll->href), 'XML::Atom::Collection';
t/10.client.t view on Meta::CPAN
ok !$client->rc;
$category = XML::Atom::Category->new;
$category->term('animal');
$category->scheme('http://example.com/cats/big3');
$entry->category($category);
ok $client->createEntry($entry_coll->href, $entry, 'Entry 1');
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
isa_ok $client->rc, 'XML::Atom::Entry';
is $client->req->slug, 'Entry 1';
is $client->res->code, RC_CREATED;
ok my $uri = $client->res->location;
$entry = $client->rc;
is $entry->title, 'Entry 1';
isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource';
# List Entry Resources (Get Feed)
ok $client->getFeed($entry_coll->href);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
isa_ok $client->rc, 'XML::Atom::Feed';
ok $client->res->is_success;
# Get Entry Resource
ok $client->getEntry($uri);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
isa_ok $client->rc, 'XML::Atom::Entry';
is $client->res->code, RC_NOT_MODIFIED;
$entry = $client->rc;
is $entry->title, 'Entry 1';
isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource';
# Update Entry Resource
$entry->title('Entry 2');
ok $client->updateEntry($uri, $entry);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
isa_ok $client->rc, 'XML::Atom::Entry';
ok $client->res->is_success;
$entry = $client->rc;
is $entry->title, 'Entry 2';
isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource';
# Delete Entry Resource
ok $client->deleteEntry($uri);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
ok !$client->rc;
ok $client->res->is_success;
# Create Media Resource
# Unsupported media type
ok !$client->createMedia($media_coll->href, 't/samples/media1.gif', 'text/plain', 'Media 1');
like $client->errstr, qr/unsupported media type/i;
ok !$client->req;
ok !$client->res;
ok !$client->rc;
ok $client->createMedia($media_coll->href, 't/samples/media1.gif', 'image/gif', 'Media 1');
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
isa_ok $client->rc, 'XML::Atom::Entry';
is $client->req->slug, 'Media 1';
is $client->res->code, RC_CREATED;
ok $uri = $client->res->location;
isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource';
# Get Media Resource
($uri) = map { $_->href } grep { $_->rel eq 'edit-media' } $client->rc->link;
ok $client->getMedia($uri);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
ok $client->rc;
ok $client->res->is_success;
is $client->res->content_type, 'image/gif';
isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource';
# Update Media Resource
ok $client->updateMedia($uri, 't/samples/media2.gif', 'image/gif');
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
ok $client->rc;
ok $client->res->is_success;
is $client->res->content_type, 'image/gif';
isa_ok $client->cache->get($uri), 'Atompub::Client::Cache::Resource';
# Delete Media Resource
ok $client->deleteMedia($uri);
isa_ok $client->req, 'HTTP::Request';
isa_ok $client->res, 'HTTP::Response';
ok !$client->rc;
ok $client->res->is_success;