0.04
No commit activity in last 3 years
No release in over 3 years
RelaxDB provides a simple interface to CouchDB
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

>= 0.9.4
>= 0
>= 0
 Project Readme

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

Overview

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.

Details

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
  
  RelaxDB::View.design_doc.save # save views to CouchDB after executing class definitions

Defining models



class User < RelaxDB::Document
  property :name
end

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| o.sender.name } ]
  
  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!"
  end
  
end


Exploring models


# Saving objects

sofa = User.new(:name => "sofa").save!
futon = User.new(:name => "futon").save!

i = Invite.new :sender => sofa, :recipient => futon, :event_name => "CouchCamp"
i.save!

# 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 ix.sender.name # prints sofa, a single CouchDB request made

# Saving with conflicts

idup = i.dup
i.save!
idup.save     # conflict printed

# Saving with and without validations

i = Invite.new :sender => sofa, :event_name => "CouchCamp"

i.save! rescue :ok      # save! throws an exception on validation failure or conflict
i.save                  # 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
i.save                                # 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
    render
  end
  
  # 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) {
    _count
  }
  $

  RelaxDB::ViewUploader.upload("view.js")
  RelaxDB.view "Invites_by_state", :key => "accepted", :reduce => true

Migrations


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

Visualise

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.