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
# 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
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
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
@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