This project is no longer under development. Visit github.com/phifty/gom¶ ↑
The intent of CouchModel is, to provide an easy interface handle CouchDB documents. It also comes with a ActiveModel implementation to integrate into an Rails 3 application.
The current version is under development and open for everyone to find bugs and post them into the issue tracker.
The code has been tested Ruby 1.8.7 and 1.9.1, CouchDB 0.10.0 and Rails 3.0.0.beta4.
Basically, the ruby standard library (1.9.1) if the only requirement. If you still using Ruby 1.8, the json gem is required.
If the activemodel gem is installed, CouchModel automatically provides an interface to Rails 3.
To run the test suite, rspec (tested with 1.2.9) is required. A CouchDB instance is only required for the integration tests (task spec:integration
).
The gem is part of the gemcutter archive. It can be installed by simply type
gem install couchmodel
To define a model, it’s necessary to create a subclass of CouchModel::Base
class User < CouchModel::Base setup_database :url => "http://localhost:5984/test", :create_if_missing => true, :delete_if_exists => false, :push_design => true key_accessor :name key_accessor :email key_accessor :language, :default => "en" key_accessor :birthday, :type => :date end
The setup_database
method defines a database for the model. The url
option is required and specifies the url of the database in the scheme [scheme]://[host]:[port]/[database_name]
. If the option create_if_missing
is set to true, CouchModel will try to create the database when the model is initialized. If the option delete_if_exists
is specified, the database will be deleted and re-created. The option push_design
will make CouchModel upload the design document assigned to the model during it’s initialization process. If the option create_if_missing
is not specified or false, the database setup be done manually by calling CouchModel::Configuration.setup_databases
and CouchModel::Configuration.setup_designs
.
The method key_accessor
defined access methods to the given keys of the CouchDB document. It’s also possible to use key_reader
and key_writer
here. If the :default
option is passed, the key will get a default value assigned during initialization of the class. The :type
option specifies the type of the attribute. Currently the types :integer
, :string
, :date
and :time
are supported.
Each defined model has a realted design document, that keeps all the views for that model. Via the command
CouchModel::Configuration.design_directory = "[directory]"
a directory is specfied that keeps all the design document. CouchModel will watch out for a file with the name [design directory]/[model_name].design
and will use it as the related design document. If no such file exists, a design document will be created (but not saved to the file). The design ducument can be asscessed via Model.design
.
A design document should look like this
id: "test_design" language: "javascript" views: view_name_1: map: function(document) { ... }; reduce: function(key, values, rereduce) { ... }; view_name_2: keys: [ "key_one", "key_two" ] ...
It will create the methods Model.view_name_1
and Model.view_name_2
, which returns the result of the related view. It’s also possible to pass some extra options like startkey
or key
to these methods.
The view can be defined by write down a map and a reduce function or provide the keys
array. If the keys
array is given, CouchModel will generate a map function that emits the given array of document keys. The reduce function will be set to null.
CouchModel also creates by default a class view. This view simply selects all documents from the corresponding model and is assigned to the method Model.all
.
CouchModel provides support for simple association definition. Currently, the method belongs_to
and has_many
are implmented.
class User < CouchModel::Base ... belongs_to :session, :class_name => "UserSession" has_many :memberships, :class_name => "Membership", :view_name => :by_user_id_and_created_at :query => proc { |created_at| { :startkey => [ self.id, (create_at || nil) ], :endkey => [ self.id, (created_at || { }) ] } } end
In this example, the belongs_to
adds a key_accessor
named session_id
to the user and also generates getters and setters for the session object itself (session
and session=
).
The has_many
acts as a wrapper for the specified view. The previously defined view by_user_id_and_created_at
emits membership-documents by thier user_id
and the created_at
date. Basically, the association can be accessed by a reader method. Options for querying the view can be passed by a hash.
user.membership(:startkey => [ ... ], :endkey => [ ... ], :descending => false)
The possible keys for that query hash can be taken from wiki.apache.org/couchdb/HTTP_view_API (Section Querying Options).
If a :query
option is defined (like in the example above), the given method is used to generate this query hash. When querying a view, the first arguments will be passed to that method and the result of the generator-method will be merged with the additionally given query hash.
user.membership(created_at, :returns => :rows)
The :returns
option extends the possible keys defined by CouchDB. If not given or specified as :models
, CouchModel will try to cast the returned rows into model classes. It also automatically passes the :include_docs
option to CouchDB. If this option is specified as :rows
, a collection of CouchModel::Row
objects is returned that wraps the CouchDB result rows. That’s maybe useful for views with a reduce function.
The following steps has been tested with the first beta version of Rails 3 (activemodel-3.0.0.beta).
First of all, the couchmodel
gem has to added to the dependencies. This can be done by adding
gem "couchmodel", :require => "couch_model"
to the Gemfile
.
The configuration can be done by creating an initializer. Here is an example file (e.g. config/initializer/couch_model.rb
).
CouchModel::Configuration.design_directory = File.join(Rails.root, "app", "models", "designs") DATABASE = { :test => { :url => "http://localhost:5984/test", :setup_on_initialization => true, :delete_if_exists => true } :development => { :url => "http://localhost:5984/development", :setup_on_initialization => true, :delete_if_exists => false }, :production => { :url => "http://localhost:5984/production", :setup_on_initialization => true, :delete_if_exists => false } }[Rails.env.to_sym] unless defined?(DATABASE)
This example uses an sub-directory of app/models
to search for the design documents. It also defined a constant named DATABASE
that is initialized with the right database setup for the each environment. This constant can then be used to define the models.
class User < CouchModel::Base setup_database DATABASE ... end
Apart from contribution, support via Flattr is welcome.