module Agnostic::Duplicate

Duplicate objects are provided with an additional method ‘duplicate` that extends the method `dup` functionality.

## When to use

The advantage of using Duplicate module reside in support for fields that are not duplicated by default for any reason. Example: when using Rails ‘dup` implementation doesn’t copy attributes of model that return an ActiveRecord::Relation, it is supossed the developer to choose his strategy.

## Usage

When using ‘Duplicate` you specify a list of attributes that you want to be copied additionaly to the object returned by `dup`. Though if `dup` returns a value for an attribute and you mark that attribute as “duplicable” then the value of the attribute will be overwritten with the value provided by `duplicate` call.

Example:

“‘ruby

class Story < ActiveRecord::Base
  include Duplicate
  # ...
  attr_duplicable :seo_element, :category, :properties
  # ...
  attr_accessible :title
  # ...
  has_one :seo_element, as: :metadatable
  has_one :category, through: :categorisation, source: :category
  has_many :properties, :images, :headlines
  # ...
end

“‘

When using ‘duplicable` over any attribute, it verifies if the current value value implements `Duplicate`. In that case it returns the result of calling to `duplicate` on that object. If the attribute doesn’t implement ‘Duplicate` it is returned the `dup` value.

If the ‘duplicable` attribute is iterable then it is returned an array where every element of the collection is duplicated following the flow defined previously.

Also it is possible to provide **shallow copies** of attribute values, modifying the default behaviour. In that case, just make use of the ‘strategy` option.

“‘ruby

attr_duplicable :images, strategy: :shallow_copy

“‘

It is given support for custom behaviour after duplication process. In that case it is only required to implement the method ‘hook_after_duplicate!`

Extending previous example:

“‘ruby

def hook_after_duplicate!(duplicate)
  duplicate.headlines = self.headlines.not_orphans.collect(&:dup)
  duplicate.images.each { |img| img.attachable = duplicate }
end

“‘

ATENTION: Observe that ‘model` passed as parameter is in fact the duplicated instance that it is going to be returned

## Configuration options

If the only attribute values you want to be duplicated are the ones you have specified through the ‘attr_duplicable` method, and though removing the additional fields duplicated because of the init call to `dup`, then you can set this configuration through `duplicable_config` method:

“‘ruby

class Image < ActiveRecord::Base
  include Duplicate
  duplicable_config new_instance: true
  # ...
  attr_duplicable :images
  # ...
end

“‘

If you want to apply the ‘duplicate` over a custom instance object instead of the default template for the current configuration, then you can pass a `dup_template` option on the method call

“‘ruby otherobject # => Object sharing duplicable attributes with ’myobject’ myobject.duplicate dup_template: otherobject “‘

As the object passed to dup_template should be compliant with the duplicable attribute list, if there is an error during the process an exception will be raise according to the type of error:

- Agnostic::Duplicate::ChangeSet::AttributeNotFound
- Agnostic::Duplicate::ChangeSet::CopyError

Constants

VERSION

Public Class Methods

included(base) click to toggle source
# File lib/agnostic/duplicate.rb, line 104
def self.included(base)
  base.extend(ClassMethods)
  base.instance_variable_set '@duplicable_changesets', []
  base.instance_variable_set '@duplicable_options', {}
end

Public Instance Methods

duplicate(opts = {}) click to toggle source

Duplicates the object @return [Duplicate] the new instance object @return opts [Hash] the options for duplicating @option [Object] dup_template The object over attributes are going to be

copied
# File lib/agnostic/duplicate.rb, line 115
def duplicate(opts = {})
  (opts[:dup_template] || dup_template).tap do |model|
    apply_changesets!(model)
    hook_after_duplicate!(model) if respond_to? :hook_after_duplicate!
  end
end

Private Instance Methods

apply_changesets!(model) click to toggle source

Applies to model the duplicable changesets defined in class definition @param model [Duplicate] the duplicated new instance object

# File lib/agnostic/duplicate.rb, line 126
def apply_changesets!(model)
  self.class.duplicable_changesets.each do |changeset|
    changeset.apply(self, model)
  end
end
dup_template() click to toggle source

@return [Duplicable] a new instance object based on global duplicable

configuration
# File lib/agnostic/duplicate.rb, line 246
def dup_template
  klass = self.class
  if klass.duplicable_option? :new_instance
    klass.new
  else
    dup
  end
end