Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
202 lines (137 sloc) 5.29 KB

What’s New?

  • 2010-05-04
    • RelaxDB 0.5 released. This is a leaner version of RelaxDB with significant performance improvements and breaking changes. See the release notes for upgrading notes.
  • 2010-04-10
    • RelaxDB 0.4 released. Supports Ruby 1.9.1 and CouchDB 0.11.0.
    • Auto-generated views no longer emit the document as a value by default
    • Erlang view shorthand supported e.g. _sum and _count
    • Added single query pagination
    • Performance improvements
    • Time.to_json fix. Thanks to Karmi
    • Note: This release includes a number of breaking changes. Please see the release notes for upgrading notes.

For those interested in using RelaxDB with an ETag based cache, please see Fred Cheung’s work


RelaxDB provides a Ruby interface to CouchDB. It offers a simple idiom for specifying object relationships. The underlying objects are persisted to CouchDB and are retreived using CouchDB idioms.

A few facilities are provided including pretty printing of GET requests and uploading of JavaScript views.

A basic merb plugin, merb_relaxdb is also available.

For more complete documentation take a look at docs/spec_results.html and the corresponding specs.


Getting started

  require 'rubygems'
  require 'relaxdb'

  RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "app"
  RelaxDB.use_db "relaxdb_scratch"
  RelaxDB.enable_view_creation  # creates views when class definition is executed # save views to CouchDB after executing class definitions

Defining models

class User < RelaxDB::Document
  property :name

class Invite < RelaxDB::Document
  property :created_at
  property :event_name
  property :state, :default => "awaiting_response",
    :validator => lambda { |s| %w(accepted rejected awaiting_response).include? s }
  references :sender, :validator => :required
  references :recipient, :validator => :required
  property :sender_name,
   :derived => [:sender, lambda { |p, o| } ]
  view_by :sender_name        # Emits 1 as the map value
  view_docs_by :sender_id     # Emits the doc as the map value
  view_by :recipient_id, :created_at, :descending => true
  def on_update_conflict
    puts "conflict!"

Exploring models

# Saving objects

sofa = => "sofa").save!
futon = => "futon").save!

i = :sender => sofa, :recipient => futon, :event_name => "CouchCamp"!

# Loading and querying

il = RelaxDB.load i._id
puts i == il # true

ir = Invite.by_sender_name "sofa" 
puts i == ir # true

ix_ids = Invite.by_sender_name :key => "sofa"
ix = ix_ids.load!.first
puts i == ix # true

# Denormalization

puts ix.sender_name # prints sofa, no requests to CouchDB made
puts # prints sofa, a single CouchDB request made

# Saving with conflicts

idup = i.dup!     # conflict printed

# Saving with and without validations

i = :sender => sofa, :event_name => "CouchCamp"! rescue :ok      # save! throws an exception on validation failure or conflict                  # returns false rather than throwing an exception
puts i.errors.inspect   # prints {:recipient=>"invalid:"}

i.validation_skip_list << :recipient  # Any and all validations may be skipped                                # succeeds

Paginating models

  # Controller

  def show(page_params={})
    uid = @user._id
    @invites = Invite.paginate_by_sender_name :startkey => [uid, {}], 
        :endkey => [uid], :descending => true, :limit => 5, :page_params => page_params
  # In your view
  <% @invites.each do |i| %>
    <%= i.event_name %>
  <% end %>
  <%= link_to "prev", "/invites/?#{@invites.prev_query}" if @invites.prev_query %>
  <%= link_to "next", "/invites/?#{@invites.next_query}" if @invites.next_query %>  

More illustrative examples are listed in the .paginate_view spec in spec/paginate_spec.rb

Creating views by hand

  $ cat view.js 
  function Invites_by_state-map(doc) {
    if(doc.relaxdb_class === "Invite")
      emit(doc.state, doc);

  // Uses the CouchDB builtin to invoke an Erlang reduce fun
  function Invites_by_state-reduce(keys, values, rereduce) {

  RelaxDB.view "Invites_by_state", :key => "accepted", :reduce => true


  $ cat 001_double.rb Primitives do |p| 
    p.num *= 2 
  $ ruby -e 'RelaxDB::Migration.run_all Dir["./*.rb"]'


Fuschia offers a web front end for visualising inter-document relationships.

Incomplete list of limitations

  • Destroying an object results in non transactional nullification of child/peer references
  • Objects can talk to only one database at a time. Similarly for design docs.