ActsAsEdition

acts_as_edition is a ruby gem for creating new editions of a tree (think document) of ActiveRecord models.

Each new edition tree is a deep clone of the previous tree. The edition tree is defined using an acts_as_edition declaration on each model in the tree. Using the resources option, acts_as_edition supports management of relationships between models in the edition tree and those outside it. A selection of hooks provide additional control over the cloning process. Conditions can be specified on models in the tree to determine when/if they are cloned. Finally, each object in the tree maintains a simple belongs_to relationship with its ancestor in the previous edition.

If you are considering acts_as_edition, you should also evaluate acts_as_versioned and deep_cloning:

github.com/technoweenie/acts_as_versioned github.com/DefV/deep_cloning

Install

gem install acts_as_edition

For ActiveRecord < 4.0.0 use:

gem install acts_as_edition --version '=1.0.1'

Example

First, add ancestor_id column to every table in the tree, using a migration thusly:

class AddEditionMigration < ActiveRecord::Migration

def self.up
  add_column :mymodel, :ancestor_id, :integer
end

def self.down
  remove_column :mymodel, :ancestor_id
end

end

rake db:migrate

Then declare acts_as_edition in the model class:

class Guide < ActiveRecord::Base

acts_as_edition :edition_chain => [:abbreviation, :imprint, :places],
                :resources => [:country, :retailers, :authors],
                :pre_hook => :unpublish_self,
                :after_clone => :increment_descendant_year,
                :post_hook => :publish_descendant
                :conditions => { :returns_true => true }
has_one :abbreviation
belongs_to :imprint
has_many :places
has_one :country
has_many :retailers
has_and_belongs_to_many :authors

protected

def unpublish_self
  self.published = false
  self.save!
end

def increment_descendant_year
  self.year = (self.year.to_i + 1).to_s
  self.save!
end

def publish_descendant
  self.descendant.published = true
  self.descendant.save!
end

def returns_true
  name != 'Noclonelandia'
end

end

class Abbreviation < ActiveRecord::Base

. . .

end

And so on – these models are taken from the tests if you need more context.

To clone:

new_edition = Guide.first.clone_edition! new_edition.ancestor # == Guide.first Guide.first.descendant # == new_edition

Attributes listed in edition_chain are cloned. Note that their model classes must also have acts_as_edition declarations.

Atributes listed in resources are not cloned. The relationship between these resource objects and objects in the previous edition tree are reestablished in the new edition tree. NOTE: the relatishionship of the resource to the original object may be broken. The exception is resources in a habtm relationship with edition objects. In these cases, the relationship to the new edition is additive.

pre_hook and post_hook are called at the beginning and end of the process on the original object. The after_clone hook is called *on the newly cloned object* before it is saved to the database.

If an object does not meet all conditions specified in the conditions options hash, clone_edition! on that object will return nil and the object will not be copied into the new tree.

Copyright © 2010, 2013 Virginia Department of Education, released under the MIT license