Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: da86969e0c
Fetching contributors…

Cannot retrieve contributors at this time

168 lines (118 sloc) 5.423 kb

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 #<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)`.

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

Jump to Line
Something went wrong with that request. Please try again.