Deep_cloneable

<img src=“https://travis-ci.org/moiristo/deep_cloneable.png?branch=master” alt=“Build Status” />

This gem gives every ActiveRecord::Base object the possibility to do a deep clone. It is a rails3 upgrade of the deep_cloning plugin (github.com/openminds/deep_cloning).

Requirements

  • Ruby 1.8.7, 1.9.2, 1.9.3, 2.0.0, 2.1.5 (tested)

  • Activerecord 3.1, 3.2, 4.0, 4.1, 4.2

  • Rails 2.x/3.0 users, please check out the ‘rails2.x-3.0’ branch.

Installation

  • In your Gemfile:

    gem ‘deep_cloneable’, ‘~> 2.1.0’

Upgrading from v1

The ‘dup’ method with arguments has been replaced in deep_cloneable 2 by the method ‘deep_clone’. Please update your sources accordingly.

Example

Cloning one single association

pirate.deep_clone :include => :mateys

Cloning multiple associations

pirate.deep_clone :include => [:mateys, :treasures]

Cloning really deep

pirate.deep_clone :include => {:treasures => :gold_pieces}

Cloning really deep with multiple associations

pirate.deep_clone :include => [:mateys, {:treasures => :gold_pieces}]

Cloning really deep with multiple associations and a dictionary

A dictionary ensures that models are not cloned multiple times when it is associated to nested models. When using a dictionary, ensure recurring associations are cloned first:

pirate.deep_clone :include => [:mateys, {:treasures => [:matey, :gold_pieces]}], :use_dictionary => true

If this is not an option for you, it is also possible to populate the dictionary manually in advance:

dict = { :mateys => {} }
pirate.mateys.each{|m| dict[:mateys][m] = m.deep_clone }
pirate.deep_clone :include => [:mateys, {:treasures => [:matey, :gold_pieces]}], :dictionary => dict

When an object isn’t found in the dictionary, it will be populated. By passing in an empty dictionary you can populate it automatically and reuse it in subsequent deep_clones to avoid creating multiples of the same object where you have overlapping associations.

Cloning a model without an attribute

pirate.deep_clone :except => :name

Cloning a model without multiple attributes

pirate.deep_clone :except => [:name, :nick_name]

Cloning a model without an attribute or nested multiple attributes

pirate.deep_clone :include => :parrot, :except => [:name, { :parrot => [:name] }]

Cloning with a block

pirate.deep_clone :include => :parrot do |original, kopy|
  kopy.cloned_from_id = original.id if kopy.respond_to?(:cloned_from_id)
end

Note: Using deep_clone with a block will also pass the associated objects that are being cloned to the block, so be sure to check whether the object actually responds to your method of choice.

Cloning without validations

pirate.deep_clone :include => {:treasures => :gold_pieces}, :validate => false

Cloning a model with only explicitly assigned attribute

pirate.deep_clone :only => :name

Cloning a model with only multiple explicitly assigned attributes

pirate.deep_clone :only => [:name, :nick_name]

Cloning a model with explicitly assigned attributes or nested multiple attributes

pirate.deep_clone :include => :parrot, :only => [:name, { :parrot => [:name] }]

Conditional cloning

pirate.deep_clone(:include => [{:treasures => { :gold_pieces => { :if => lambda{|piece| piece.is_a?(Parrot) } } } }, :mateys => { :unless => lambda{|matey| matey.is_a?(GoldPiece) } } ])

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2015 Reinier de Lange. See LICENSE for details.