# 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 ## Quick Start 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 => 'john@email.com' }, '123' ) doc.email # 'john@email.com' doc.save! # { # '_id': 'user::123', # '_type': 'user', # '_created_at': '2014-07-04 00:00:00 UTC' # '_updated_at': '2014-07-04 00:00:00 UTC' # 'email': 'john@email.com', # } ### Document-Level Directives The following are directives that can be used to trigger specific behaviors at the Document level. * `type T` Set the type of the Document. * `type_prefix true|false` Default to `true`. If set to false, it removes prefixing document id with the document type. Leaving this to true is the recommended behavior to avoid id conflicts between different types of documents, especially when custom ids are being used. * `attribute name, &block` Declares an attribute for this Document. You can specify additional directives for each attribute. See Attributes section. * `db connection` Sets the database connections. If set once, it will set that connection as the primary connection. Any subsequent calls to this directive will set those connections as secondary connections. See Multiple DB Connections section. * `rename` Rename keys. See Migration section. * `migrate` Migrate the value of a key. This gets triggered when Document is initialized. See Migration section. ### Attributes Using Attribute Directive: 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 = 'john@email.com' 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 => 'john@email.com' } ) 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 those DB connection as secondary connections, which will only 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. You can also migrate the values. The `migrate` directive is trigger when Document is initialized, but after the `rename`. This is useful if you have Documents whose values are in the old format and you want to convert them. class User < CouchPillow::Document rename :nickname => :nicknames attribute :nicknames migrate :nicknames do |v| v.class == String ? [v] : v end end u = User.new( { :nickname => 'jdoe' } ) u.nicknames # ['jdoe'] ## 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 => 'john@email.com').map do |v| User.new(v.doc, v.id) end