{}[http://travis-ci.org/rubiety/has_draft]
{}[https://gemnasium.com/rubiety/has_draft]
{}[https://codeclimate.com/github/rubiety/has_draft]
== Has Draft
Allows for multiple "drafts" of a model which can be useful when developing:
* Draft/Live Version of Pages, for examples
* A workflow system whereby a live copy may need to be active while a draft copy is
awaiting approval.
This was built to be able to be tacked on to existing models, so the data schema doesn't need to change
at all for the model this is applied to. Drafts are actually stored in a nearly-identical table
and there is a has_one relationship to this. This separation allows the base model to really be treated
just as before without having to apply conditions in queries to make sure you are really getting the
"live" (non-draft) copy: Page.all will still only return the non-draft pages. This separate table is backed by
a model created on the fly as a constant on the original model class. For example if a Page has_draft,
a Page::Draft class will exist as the model for the page_drafts table.
== Installation
This gem requires ActiveRecord 3.0.
In your Gemfile:
gem "has_draft"
== Basic Example
## First Migration (If Creating base model and drafts at the same time):
class InitialSchema < ActiveRecord::Migration
[:articles, :article_drafts].each do |table_name|
create_table table_name, :force => true do |t|
t.references :article if table_name == :article_drafts
t.string :title
t.text :summary
t.text :body
t.date :post_date
end
end
end
## Model Class
class Article < ActiveRecord::Base
has_draft
end
## Exposed Class Methods & Scopes:
Article.draft_class
=> Article::Draft
Article.with_draft.all
=> (Articles that have an associated draft)
Article.without_draft.all
=> (Articles with no associated draft)
## Usage Examples:
article = Article.create(
:title => "My Title",
:summary => "Information here.",
:body => "Full body",
:post_date => Date.today
)
article.has_draft?
=> false
article.instantiate_draft!
article.has_draft?
=> true
article.draft
=> Article::Draft Instance
article.draft.update_attributes(
:title => "New Title"
)
article.replace_with_draft!
article.title
=> "New Title"
article.destroy_draft!
article.has_draft?
=> false
== Custom Options
## First Migration (If Creating base model and drafts at the same time):
class InitialSchema < ActiveRecord::Migration
[:articles, :article_copies].each do |table_name|
create_table table_name, :force => true do |t|
t.integer :news_article_id if table_name == :article_copies
t.string :title
t.text :summary
t.text :body
t.date :post_date
end
end
end
## Model Class
class Article < ActiveRecord::Base
has_draft :class_name => 'Copy', :foreign_key => :news_article_id, :table_name => 'article_copies'
end
== Method Callbacks
There are three callbacks you can specify directly as methods:
class Article < ActiveRecord::Base
has_draft
def before_instantiate_draft
# Do Something
end
def before_replace_with_draft
# Do Something
end
def before_destroy_draft
# Do Something
end
end
== Extending the Draft Class
Because you don't directly define the draft class, you can specify a block of code to be run in its
context by passing a block to has_draft:
class Article < ActiveRecord::Base
belongs_to :user
has_draft do
belongs_to :last_updated_user
def approve!
self.approved_at = Time.now
self.save
end
end
end
== Running Tests
This gem uses appraisal to test with different versions of the dependencies. See Appraisal first for which versions are tested.
# Just the gems locked in Gemfile.lock
$ bundle exec rake test
# All of the Appraisals:
$ bundle exec rake all