Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/candlerb/couchtiny

A tiny CouchDB API for Ruby
https://github.com/candlerb/couchtiny

Last synced: about 2 months ago
JSON representation

A tiny CouchDB API for Ruby

Awesome Lists containing this project

README

        

= CouchTiny

This library is a tiny CouchDB API inspired by CouchRest, aiming to be lean,
simple to understand, and closely aligned to CouchDB's own API. It also
provides pluggable interfaces for JSON parsing, UUID generation and HTTP
client.

== Dependencies

CouchTiny depends on the 'json' package, and also currently 'restclient',
although I intend at some stage to have a replacement client based
directly on Net::HTTP.

== Low level (database-centric) API

This is implemented in CouchTiny::Database and CouchTiny::Server. All
documents are passed in and out as plain hashes.

require 'rubygems'
require 'couchtiny'
db = CouchTiny::Database.url("http://127.0.0.1:5984/foo")
db.create_database!

doc = {"hello"=>"world"}
db.put doc
puts doc['_id']
puts doc['_rev']

puts db.get(doc['_id']).inspect

puts db.server.stats.inspect
db.server.restart!

If you are accessing multiple databases on the same server, it is more
efficient to share a Server object between them.

require 'rubygems'
require 'couchtiny'
serv = CouchTiny::Server.new :url=>"http://127.0.0.1:5984"
db1 = CouchTiny::Database.new(serv, "foo")
db2 = CouchTiny::Database.new(serv, "bar")
... etc

=== JSON parser plugin example

Assuming you have an object which implements .parse and .unparse methods,
you can hook it into the low-level API like this:

require 'couchtiny/parser/jsobject'
db = CouchTiny::Database.url("http://127.0.0.1:5984/testdb",
:parser => CouchTiny::Parser::JSObject)
doc = db.get("12345")
puts doc.foo

Here, the JSObject parser makes hashes behave like Javascript objects - that
is, you can access them as doc.foo or doc['foo'] equally.

Such options can be set globally:

CouchTiny::Server.options[:parser] = CouchTiny::Parser::JSObject

=== HTTP plugin

The HTTP backend is also pluggable. The default one uses RestClient but
patches the error message from the JSON body into the exception message.
This is easily replaced if you want.

=== UUID plugin

See CouchTiny::UUIDS::Time for a time-based UUID generator, which has the
benefit that document IDs are allocated in increasing order.

== High-level (document-centric) API

This adds a little object sugar to CouchDB. This is an independent layer, in
the sense that the low-level API never makes any call back to the high-level
API.

=== CouchTiny::Document

class Foo < CouchTiny::Document
use_database CouchTiny::Database.url("http://127.0.0.1:5984/foo")

define_view "Foo_by_bar", <<-MAP, :include_docs=>true
function(doc) {
if (doc.type == 'Foo' && doc.bar) {
emit(doc.bar, null);
}
}
MAP
end

p Foo.all
p Foo.on_db(another_db).count
p Foo.view_foo_by_bar(:key=>"xxx")

CouchTiny::Document doesn't expose flat "properties" (*), nor perform any
validation - the latter is probably best done in a validate_doc_update
function (**). But it does allow you to associate a document with a database
and save it back to that database; to create classes of documents; to
associate those classes with a definable 'type' attribute; and to
instantiate objects from views.

If you call Foo.get(id) but the retrieved document has "type":"Bar" then you
will get an instance of Bar. Similarly, a view can return a polymorphic
collection of objects, and each one will be instantiated correctly.

However, Foo.all and Foo.count will count only actual instances of Foo, not
subclasses of Foo.

(*) Note that in a web framework, you can usually get away with using
Hash#update:

foo = Foo.get(params[:id])
foo.update(params[:foo])
foo.save

To be able to use Rails form helpers, you may get away with:

class Foo
auto_accessor
end

=== CouchTiny::Design

CouchTiny::Design is a wrapper for creating design documents, using a 'slug'
so that when you run a new version of your program it generates a separate
design document, allowing time for the new views to be built before
switching over users. Note that unlike CouchRest, by default you will get
one design doc for your whole app, not one per class. There is a single
"all" view which lets you query and count documents by type.

(**) The slugged design docs are good when modifying the views in
applications, but not so good for validate_doc_update functions, because:

(a) if there are multiple design docs with validate_doc_update they will
*all* be used on every document; and

(b) the design doc isn't saved until a view is first used, so initially the
user may bypass validation entirely.

So it's better to create a separate design doc purely for
validate_doc_update, under a fixed name, and save it at application startup
time (or if you have a separate database per user, when the user logs in)

== Compatibility problems

ActiveSupport 2.3.2 and 2.3.2.1 break JSON in one way, and ActiveSupport
2.3.4 breaks it in another way (breaking JSON.unparse and JSON.pretty_generate).
The problem shows itself like this:

NoMethodError: undefined method `[]' for #
from /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.2/lib/active_support/json/encoders/hash.rb:34:in `to_json'

As a workaround, I have now changed CouchTiny to use `obj.to_json` instead
of `JSON.unparse(obj)`.

== Licence

This software is released under the same terms as Ruby. See the files
COPYING and GPL.

== Author

Brian Candler (B dot Candler at pobox.com dot com)

Copyright (C) 2009