- 2009-05-27
- Added minimal support for data migrations. Although CouchDB’s nature removes the necessity for migrations, certain knowledge that all objects possess a particular property can simplify client logic. This desire for simplification is the rationale behind this change.
- 2009-04-19
- Defaults to taf2-curb, falling back to Net/HTTP if it taf2-curb can’t be loaded. Thanks to Fred Cheung.
- For those interested in using RelaxDB with an ETag based cache, look here
- 2009-03-31
- RelaxDB 0.3 released – compatible with CouchDB 0.9.
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.
Note: While RelaxDB 0.3 is explicitly compatible with CouchDB 0.9, HEAD typically tracks CouchDB HEAD.
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
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
view_by :sender_id
view_by :recipient_id, :created_at, :descending => true
def on_update_conflict
puts "conflict!"
end
end
# 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 = Invite.by_sender_name(:key => "sofa").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
# 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
$ cat view.js
function Invites_by_state-map(doc) {
if(doc.relaxdb_class === "Invite")
emit(doc.state, doc);
}
function Invites_by_state-reduce(keys, values, rereduce) {
if (rereduce) {
return sum(values);
} else {
return values.length;
}
}
$
RelaxDB::ViewUploader.upload("view.js")
RelaxDB.view "Invites_by_state", :key => "accepted", :reduce => true
$ cat 001_double.rb
RelaxDB::Migration.run Primitives do |p|
p.num *= 2
p
end
$ ruby -e 'RelaxDB::Migration.run_all Dir["./*.rb"]'
Fuschia offers a web front end for visualising inter-document relationships.
- 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.