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.
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.
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
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 equally.
Such options can be set globally:
CouchTiny::Server.options[:parser] = CouchTiny::Parser::JSObject
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.
See CouchTiny::UUIDS::Time for a time-based UUID generator, which has the benefit that document IDs are allocated in increasing order.
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.
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 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)
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 #<JSON::Ext::Generator::State:0xb7814eb4>
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)`.
This software is released under the same terms as Ruby. See the files COPYING and GPL.
Brian Candler (B dot Candler at pobox.com dot com)
Copyright © 2009