Skip to content
This repository

A convenience wrapper around CouchDB (using CouchPotato) that supports relations and S3 attachments

tree: d79c1b5825

Fetching latest commit…

Cannot retrieve the latest commit at this time


Development work as stopped as we don't use SimplyStored anymore. Please do not expect any future commits and fixes.


Convenience layer for CouchDB on top of CouchPotato.

SimplyStored allows you to persist your objects to CouchDB using an ActiveRecord-like syntax.

In contrast to CouchPotato (on top of it is build) it supports associations and other syntactic sugar that makes ActiveRecord so appealing.

SimplyStored has also support for S3 attachments.

See also RockingChair on how to speed-up your unit tests by using an in-memory CouchDB backend.

More examples on how to work with SimplyStored can be found here


gem install simply_stored

Using with Rails

Create a config/couchdb.yml:

default: &default
  validation_framework: :active_model # optional
  split_design_documents_per_view: true # optional

  <<: *default
  database: http://localhost:5984/development_db
  <<: *default
  database: http://localhost:5984/test_db
  <<: *default
  database: <%= ENV['DB_NAME'] %>

Rails 2.x

In config/environment.rb:

config.gem 'simply_stored', :source => ''
config.frameworks -= [:active_record] # if you do not need ActiveRecord any more

Please note that if you use Rails 2.x, you can only use SimplyStored until version 0.5.4. SimplyStored 0.6 and above require Rails 3.x.

Rails 3.x

Add to your Gemfile:

# gem 'rails' # we don't want to load activerecord so we can't require rails
gem 'railties'
gem 'actionpack'
gem 'actionmailer'
gem 'activemodel'
gem 'simply_stored', :require => 'simply_stored/couch'

Please also see the installation info of CouchPotato


Require SimplyStored:

require 'simply_stored'
CouchPotato::Config.database_name = ""

From now on you can define classes that use SimplyStored.


SimplyStored auto-generates views for you and handles all the serialization and de-serialization stuff.

class User
  include SimplyStored::Couch

  property :login
  property :age
  property :accepted_terms_of_service, :type => :boolean
  property :last_login, :type => Time

user = => 'Bert', :age => 12, :accepted_terms_of_service => true, :last_login =

# => 'Bert'

# => [user]

class Post
  include SimplyStored::Couch

  property :title
  property :body

  belongs_to :user

class User
  has_many :posts

post = Post.create(:title => 'My first post', :body => 'SimplyStored is so nice!', :user => user)

# => [post]

Post.find_all_by_title_and_user_id('My first post',
# => 'SimplyStored is so nice!'


user.posts(:force_reload => true)
# => []


The supported associations are: belongs_to, has_one, has_many, has_many :through, and has_and_belongs_to_many:

class Post
  include SimplyStored::Couch

  property :title
  property :body

  has_many :posts, :dependent => :destroy
  has_many :users, :through => :posts
  belongs_to :user

class Comment
  include SimplyStored::Couch

  property :body

  belongs_to :post
  belongs_to :user

post = Post.create(:title => 'Look ma!', :body => 'I can have comments')

mike = User.create(:login => 'mike')
mikes_comment = Comment.create(:user => mike, :post => post, :body => 'Wow, comments are nice')

john = User.create(:login => 'john')
johns_comment = Comment.create(:user => john, :post => post, :body => 'They are indeed')

# => [mikes_comment, johns_comment]

post.comments(:order => :desc)
# => [johns_comment, mikes_comment]

post.comments(:limit => 1)
# => [mikes_comment]

# => 2

# => [mike, john]

# => 2

n:m relations where the IDs are stored on one part as an array:

class Server
  include SimplyStored::Couch

  property :hostname

  has_and_belongs_to_many :networks, :storing_keys => true

class Network
  include SimplyStored::Couch

  property :klass

  has_and_belongs_to_many :servers, :storing_keys => false

network = Network.create(:klass => "A")
server = => '')
server.network_ids # => []
network.servers # => [server]
server.networks # => [network]

The array property holding the IDs of the other item will be used to constuct two view to lookup the other part. Soft deleting is only supported on the class holding the IDs.

Custom Associations

class Document
  include SimplyStored::Couch

  belongs_to :creator, :class_name => "User"
  belongs_to :updater, :class_name => "User"

d =
d.creator = User.first


Further, you can have validations (using the validatable gem)

class Project
  include SimplyStored::Couch

  property :title
  property :budget
  property :deadline, :type => Time
  property :priority

  validates_presence_of :budget
  validates_uniqueness_of :priority
  validates_format_of :title, :with => /\A[a-z0-9\-']+\Z/, :allow_blank => true
  validates_inclusion_of :priority, :in => [0,1,2,3,4,5]


project =
# => false

# => #<Validatable::Errors:0x102592740 @errors={:budget=>["can't be empty"], :priority=>["must be one or more of 0, 1, 2, 3, 4, 5"]}!
# => raises CouchPotato::Database::ValidationsFailedError: #<CouchPotato::Database::ValidationsFailedError:0x102571130>

S3 Attachments

SimplyStored supports storing large attachments in Amazon S3. It uses RightAWS for the interaction with the EC2 API:

class Log
  include SimplyStored::Couch
  has_s3_attachment :data, :bucket => 'the-bucket-name',
                           :access_key => 'my-AWS-key-id',
                           :secret_access_key => 'psst!-secret',
                           :location => :eu,
                           :after_delete => :delete,
                           :logger =>'/dev/null')


log = ='/var/log/messages')
# => true

# => 11238132

This will create an item on S3 in the specified bucket. The item will use the ID of the log object as the key and the body will be the data attribute. This way you can store big files outside of CouchDB.

Soft delete

SimplyStored also has support for "soft deleting" - much like acts_as_paranoid. Items will then not be deleted but only marked as deleted. This way you can recover them later.

class Document
  include SimplyStored::Couch

  property :title
  enable_soft_delete # will use :deleted_at attribute by default

doc = Document.create(:title => 'secret project info')
Document.find_all_by_title('secret project info')
# => [doc]


Document.find_all_by_title('secret project info')
# => []

Document.find_all_by_title('secret project info', :with_deleted => true)
# => [doc]

CouchDB - Auto resolution of conflicts on save

SimplyStored now by default retries conflicted save operations if it is possible to resolve the conflict. Solving the conflict means that if updated were done one different attributes the local object will refresh those attributes and try to save again. This will be tried two times by default. Afterwards the conflict exception will be re-raised.

This feature can be controlled on the class level like this: User.auto_conflict_resolution_on_save = true | false

If auto_conflict_resolution_on_save is enabled, something like this will work:

class Document
  include SimplyStored::Couch

  property :title
  property :content

original = Document.create(:title => 'version 1', :content => 'Hi there')

other_client = Document.find(

original.title = 'version 2'!

other_client.content = 'A better version'!  # -> this line would fail without auto_conflict_resolution_on_save

# => 'version 2'


SimplyStored is licensed under the Apache 2.0 license. See LICENSE.txt


SimplyStored was written by Mathias Meyer and Jonathan Weiss for Peritor.

Something went wrong with that request. Please try again.