Fork me on GitHub

CouchRest and CouchRest Model

Embedded or Nesting Models

The CouchRest Model Embeddable module allows you to take full advantage of CouchDB’s ability to store complex documents and retrieve them. Simply include the module in a Class and set any properties you’d like to use. For example:

class CatToy
  include CouchRest::Model::Embeddable

  property :name, String
  property :purchased, Date
end

class Cat < CouchRest::Model::Base
  property :name, String
  property :toys, CatToy, :array => true
end

@cat = Cat.new(:name => 'Felix', :toys => [{:name => 'mouse', :purchased => 1.month.ago}])
@cat.toys.first.class == CatToy
@cat.toys.first.name == 'mouse'

Any hashes sent to the property will be automatically converted:

@cat.toys << {:name => 'catnip ball'}
@cat.toys.last.is_a?(CatToy) # True!

Obviously, to avoid Ruby raising a missing constant error, classes must be defined before the parent uses them. To avoid this, or if you have a simple set of data to model, CouchRest Model supports creating anonymous classes:

class Cat < CouchRest::Model::Base
  property :name, String

  # define a property with a nested anonymous array of embeddable models
  property :toys, :array => true do
    property :name, String
    property :rating, Integer
  end
end

# Inialize a new Cat with nested toys
@cat = Cat.new(
  :name => 'Felix', 
  :toys => [
    {:name => 'mouse', :rating => 3},
    {:name => 'catnip ball', :rating => 5}
  ])

# Access the values
@cat.toys.last.rating   #=> 5
@cat.toys.last.name     #=> 'catnip ball'

# Update them in place
@cat.toys.first.rating = 2

The example above defines an array of the annonymous class on the toys attribute, but it is often useful to group data into a simple hash:

class Journey < CouchRest::Model::Base
  property :title, String
  property :start do
    property :name, String
    property :loc,  Point
    property :time, Time
  end
end

@journey = new Journey(
  :title => "Sample Journey",
  :start => {
    :name => "Calle San Bernardo 13",
    :loc  => [40.4221, -3.7081],
    :time => "2013-07-24T16:44:23Z"
  }
)

@journey.start.name   # => "Calle San Bernardo 13"

If you prefer a more traditional usage of blocks and avoid the magical instance_eval, a block can be provided with a parameter. This might be useful if you need to access a variable outside of the block as the scope will not have been altered.

class Cat < CouchRest::Model::Base
  # use a traditional block to set add properties in nested array
  property :toys, :array => true do |toy|
    toy.property :name, String
    toy.property :rating, Integer
  end
end
blog comments powered by Disqus