AnyEvent-CouchDB
view release on metacpan or search on metacpan
lib/AnyEvent/CouchDB.pm view on Meta::CPAN
package AnyEvent::CouchDB;
use strict;
use warnings;
our $VERSION = '1.31';
use JSON;
use AnyEvent::HTTP;
use AnyEvent::CouchDB::Database;
use AnyEvent::CouchDB::Exceptions;
use URI;
use URI::Escape;
use File::Basename;
use MIME::Base64;
use Exporter;
use base 'Exporter';
our @EXPORT = qw(couch couchdb);
# exception class shortcuts
our $HTTPError = "AnyEvent::CouchDB::Exception::HTTPError";
our $JSONError = "AnyEvent::CouchDB::Exception::JSONError";
# default JSON encoder
our $default_json = JSON->new->allow_nonref->utf8;
# arbitrary uri support
sub _build_headers {
my ( $self, $options ) = @_;
my $headers = $options->{headers};
if ( ref($headers) ne 'HASH' ) {
$headers = {};
}
# should probably move $options->{type} to $options->{headers}
if ( exists $options->{type} ) {
$headers->{'Content-Type'} = $options->{type};
}
elsif ( !exists $headers->{'Content-Type'} ) {
$headers->{'Content-Type'} = 'application/json';
}
if ( exists $self->{http_auth} ) {
$headers->{'Authorization'} = $self->{http_auth};
}
return $headers;
}
# return a condvar and callback
#
# - The condvar is what most of our methods return.
# You can call recv on them to get data back, or
# you can call cb on them to assign an asynchronous callback to
# run WHEN the data comes back
#
# - The callback is the code that handles the
# generic part of every CouchDB response. This is given
# to AnyEvent::HTTP.
#
sub cvcb {
my ($options, $status, $json) = @_;
$status ||= 200;
$json ||= $default_json;
my $cv = AE::cv;
AE::now_update();
# default success handler sends back decoded json response
my $success = sub {
my ($resp) = @_;
$options->{success}->(@_) if ($options->{success});
$cv->send($resp);
};
# default error handler croaks w/ http headers and response
my $error = sub {
my ($headers, $response) = @_;
$options->{error}->(@_) if ($options->{error});
$cv->croak(
$HTTPError->new(
message => sprintf("%s - %s - %s", $headers->{Status}, $headers->{Reason}, $headers->{URL}),
headers => $headers,
body => $response
)
);
};
my $cb = sub {
my ($body, $headers) = @_;
my $response;
eval { $response = $json->decode($body); };
$cv->croak(
$JSONError->new(
message => $@,
headers => $headers,
body => $body
)
) if ($@);
if ($headers->{Status} >= $status and $headers->{Status} < 400) {
$success->($response);
} else {
$error->($headers, $body);
}
};
($cv, $cb);
}
sub couch {
AnyEvent::CouchDB->new(@_);
}
sub couchdb {
my $db = shift;
if ($db =~ /^https?:/) {
lib/AnyEvent/CouchDB.pm view on Meta::CPAN
$cv;
}
sub replicate {
my ($self, $source, $target, $options) = @_;
my ($cv, $cb) = cvcb($options);
my $replication = {source => $source, target => $target};
if (my $continuous = delete $options->{continuous}) {
$replication->{continuous} = 1;
}
my $body = $default_json->encode($replication);
http_request(
POST => $self->{uri}.'_replicate',
headers => $self->_build_headers($options),
body => $body,
$cb
);
$cv;
}
1;
__END__
=head1 NAME
AnyEvent::CouchDB - a non-blocking CouchDB client based on jquery.couch.js
=head1 SYNOPSIS
Getting information about a CouchDB server:
use AnyEvent::CouchDB;
use Data::Dump 'pp';
my $couch = couch('http://localhost:5984/');
print pp( $couch->all_dbs->recv ), "\n";
print pp( $couch->info->recv ), "\n";
Get an object representing a CouchDB database:
my $db = $couch->db('database');
$db = couchdb('database');
$db = couchdb('http://somewhere.com:7777/database/');
With authentication:
# user is the username and s3cret is the password
$db = couchdb('http://user:s3cret@somewhere.com:7777/database');
Work with individual CouchDB documents;
my $user = $db->open_doc('~larry')->recv;
$user->{name} = "larry";
$db->save_doc($user)->recv;
Query a view:
$db->view('users/all', { startkey => 'b', endkey => 'bZZZ' })->recv
Finally, an asynchronous example:
# Calling cb allow you to set a callback that will run when results are available.
$db->all_docs->cb(sub {
my ($cv) = @_;
print pp( $cv->recv ), "\n";
});
# However, you have to be in an event loop at some point in time.
AnyEvent->condvar->recv;
=head1 DESCRIPTION
AnyEvent::CouchDB is a non-blocking CouchDB client implemented on top of the
L<AnyEvent> framework. Using this library will give you the ability to run
many CouchDB requests asynchronously, and it was intended to be used within
a L<Coro>+L<AnyEvent> environment. However, it can also be used synchronously
if you want.
Its API is based on jquery.couch.js, but we've adapted the API slightly so that
it makes sense in an asynchronous Perl environment.
=head2 AnyEvent condvars
The main thing you have to remember is that all the data retrieval methods
return an AnyEvent condvar, C<$cv>. If you want the actual data from the
request, there are a few things you can do.
You may have noticed that many of the examples in the SYNOPSIS call C<recv>
on the condvar. You're allowed to do this under 2 circumstances:
=over 4
=item Either you're in a main program,
Main programs are "allowed to call C<recv> blockingly", according to the
author of L<AnyEvent>.
=item or you're in a Coro + AnyEvent environment.
When you call C<recv> inside a coroutine, only that coroutine is blocked
while other coroutines remain active. Thus, the program as a whole is
still responsive.
=back
If you're not using Coro, and you don't want your whole program to block,
what you should do is call C<cb> on the condvar, and give it a coderef to
execute when the results come back. The coderef will be given a condvar
as a parameter, and it can call C<recv> on it to get the data. The final
example in the SYNOPSIS gives a brief example of this.
Also note that C<recv> will throw an exception if the request fails, so be
prepared to catch exceptions where appropriate.
Please read the L<AnyEvent> documentation for more information on the proper
use of condvars.
=head2 The \%options Parameter
Many data retrieval methods will take an optional C<\%options> hashref.
Most of these options get turned into CGI query parameters. The standard
CouchDB parameters are as follows:
=over 4
=item key=keyvalue
This lets you pick out one document with the specified key value.
=item startkey=keyvalue
This makes it so that lists start with a key value that is greater than or
equal to the specified key value.
=item startkey_docid=docid
This makes it so that lists start with a document with the specified docid.
=item endkey=keyvalue
( run in 0.951 second using v1.01-cache-2.11-cpan-bbe5e583499 )