candlerb / couchtiny
- Source
- Commits
- Network (1)
- Issues (0)
- Downloads (0)
- Wiki (1)
- Graphs
-
Branch:
master
| name | age | message | |
|---|---|---|---|
| |
.gitignore | Mon Apr 06 00:56:47 -0700 2009 | |
| |
COPYING | Fri Jun 26 02:47:27 -0700 2009 | |
| |
GPL | Fri Jun 26 02:47:27 -0700 2009 | |
| |
README.rdoc | Thu Nov 12 05:41:45 -0800 2009 | |
| |
Rakefile | Wed May 27 13:54:19 -0700 2009 | |
| |
TODO | Fri Jun 26 01:11:21 -0700 2009 | |
| |
doc/ | Fri Jun 26 02:02:06 -0700 2009 | |
| |
lib/ | Thu Nov 12 04:59:40 -0800 2009 | |
| |
test/ | Thu Nov 12 04:59:40 -0800 2009 |
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
