CouchPillow

CouchPillow is a document integrity tool for Couchbase Documents to make sure that all current and existing documents can work nicely with the current code.

CouchPillow separates itself from the database drivers, making it light and independent from the implementation. Although it is initially designed to work with Couchbase Server, it can be easily extended to other NoSQL databases, by creating a driver that respond_to? the set, delete, replace, and get methods.

Features

  • Automatic id generation.
  • Automatic timestamp.
  • Built-in and custom data validations.

Installation

gem install couchpillow

How To Use

require 'couchpillow'

class MyDocument < CouchPillow::Document
  type :my_document
  attribute :stuff
end

CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
doc = CouchPillow::Document.new( { :stuff => 'hello' } )
doc.save!

# {
#   '_id': 'my_document::fb579b265cc005c47ff420a5c2a15d2b',
#   '_type': 'my_document',
#   '_created_at': '2014-07-04 00:00:00 UTC'
#   '_updated_at': '2014-07-04 00:00:00 UTC'
#   'stuff': 'hello',
# }

Retrieving Documents:

doc = MyDocument.get('my_document::fb579b265cc005c47ff420a5c2a15d2b')
doc.stuff # 'hello'

Specifying custom id:

class User < CouchPillow::Document
  type :user
  attribute :email
end

CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
doc = User.new( { :email => '[email protected]' }, '123' )
doc.email # '[email protected]'
doc.save!

# {
#   '_id': '123',
#   '_type': 'user',
#   '_created_at': '2014-07-04 00:00:00 UTC'
#   '_updated_at': '2014-07-04 00:00:00 UTC'
#   'email': '[email protected]',
# }

Attributes

Using Attribute Directives:

class User < CouchPillow::Document
  type :user

  attribute :email do
    required
  end

  attribute :first_name do
    type String
  end
end

CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
doc = User.new( { :first_name => 'John' } )
doc.save! # raises ValidationError "Attribute 'email' is missing"
doc.email = '[email protected]'
doc.save! # Success!

List of Attribute Directives:

  • required

Sets this attribute as required. Will raise an error if attribute is missing upon save! or update!

  • type(T)

Sets the type of this attribute. Will perform type check if specified.

  • auto_convert

Enables auto-conversion to the specified type. This gets ignored if type directive is not specified.

  • default(&block)

Runs the block to set the default value for this attribute, if it's missing or nil. This is triggered on document creation and save.

  • content(&block)

Custom validation method to check the value of the attribute. This is useful in cases where you only want certain values to be stored (e.g a number between 1-10 only)

TTL Support

TTL is supported by passing options when saving the document. Using the above example:

doc = User.new( { :email => '[email protected]' } )
doc.save! ttl: 3600

Multiple DB Connections Support

If you have a model that's accessing a different Couchbase bucket, or a different Couchbase DB cluster entirely, you can specify the connections via the db directive. Example:

CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
memcached = Couchbase.connect( bucket: 'mymemcache_bucket', host: '128.128.128.128' )

class User < CouchPillow::Document
  type :user

  attribute :first_name do
    type String
  end
end

class Token < CouchPillow::Document
  type :token

  db memcached

  attribute :token do
    type String
  end
end

doc = User.new( { :first_name => 'John' } )
doc.save! # This gets saved to the localhost/bucket

token = Token.new( token: SecureRandom.uuid )
doc.save! # This gets saved to the 128.128.128.128/mymemcache_bucket

You can also specify multiple db directives. The first time the db directive is called, it sets that connection as the primary connection, as the above example shows. Any subsequent calls will insert that DB connection as secondary connections, which will only be trigger on write (save!, update!, and delete!).

class Token < CouchPillow::Document
  type :token

  db primary_connection
  db migration
  db backup

  attribute :token do
    type String
  end
end

This can be useful as part of a migration process where you want to save incoming data to another cluster while keeping the old one active.

Migration

Using rename to rename keys. Useful to maintain document integrity after a migration.

class User < CouchPillow::Document
  rename :username, :nickname
  attribute :nickname
end
u = User.new( { :username => 'jdoe' } )
u.nickname # 'jdoe'

Rename triggers per-document basis. You can use this on a separate script that queries each document in the database and updates them, or you can simply use this inside your application code, so it only migrates the document as it reads them.

Design Docs and Views

Design Docs and Views are outside the scope of CouchPillow. However, given a design doc named my_design_doc and a View named by_email, that returns documents as values, you can easily use it like this:

CouchPillow.db.design_docs['my_design_doc'].
  by_email(:key => '[email protected]').map do |v|
    User.new(v.doc, v.id)
  end