ActiveRecord DeepClone Plugin

written by Jan on March 27th, 2008 @ 09:19 PM

Dolly

ActiveRecord::Base already has an implementation of a “shallow” clone. From it’s documentation:

  # Returns a clone of the record that hasn't been assigned an id yet and
  # is treated as a new record.  Note that this is a "shallow" clone:
  # it copies the object's attributes only, not its associations.
  # The extent of a "deep" clone is application-specific and is therefore
  # left to the application to implement according to its need.

And this implementation is fine by default. However, in a few application I’ve seen myself writing the same deep-clone code over and over again. When I take a look at all those implementations, my needs are quite the same most of the time, cloning some associations, and excluding (clearing) some attributes.

For this I’ve written the DeepClone plugin, which allows you to do just that, including associations and excluding attributes. A real-life example:

  class Invoice < ActiveRecord::base
    has_many :invoice_items
    belongs_to :client

    def to_param
      iid # InvoiceID, eg IV08002
    end
  end

The application needed a way to copy an invoice, including all it’s invoiceitems, but not the iid, or the clientid. The plugin makes this really easy.

  @invoice = Invoice.find_by_iid('IV08001')
  @new_invoice = @invoice.clone :include => :invoice_items,
                                            :except => [:iid, :client_id]

Which does exactly what I need. The plugin repository can be found at http://github.com/openminds/deep_cloning/tree/master

When you’re on Rails Trunk, you can install the plugin with:


  script/plugin install git://github.com/openminds/deep_cloning.git


Comments

  • Bob on 02 Apr 16:36

    Great stuff! I've also rolled my own deep clones, but from now on, i'll be using your plugin..

    Keep up the good work!

    Cheers, Bob, Sweden

  • Mark Donnelly on 16 Apr 18:28

    This is nice.

    However, the plugin doesn't handle included associations with hashes enclosed within arrays:

    :include => [:a, {:b => :c}]

    Here's a patch to do that:

    @@ -36,6 +36,10 @@

     if options[:include]
       Array(options[:include]).each do |association, deep_associations|
    
    • if (association.kind_of? Hash)
    • deep_associations = association[association.keys.first]
    • association = association.keys.first
    • end opts = deepassociations.blank? ? {} : {:include => deepassociations} kopy.send("#{association}=", self.send(association).collect {|i| i.clone(opts) }) end
  • Mark Donnelly on 16 Apr 18:33

    Hmm. The text editor seems to have eaten my patch. Let me try that again:

    @@ -36,6 +36,10 @@

     if options[:include]
       Array(options[:include]).each do |association, deep_associations|
    
    • if (association.kind_of? Hash)
    • deep_associations = association[association.keys.first]
    • association = association.keys.first
    • end opts = deepassociations.blank? ? {} : {:include => deepassociations} kopy.send("#{association}=", self.send(association).collect {|i| i.clone(opts) }) end
  • Adrian on 17 Nov 05:35

    Hi! Great plugin, but I had to do small modification:

    I have models: Design and Background. Design belongsto model and Background hasmany Designs. When I type:

    design.clone :include [:background]

    I get an error that Background does not have collect method. I solve this doing:

    if self.send(association).respond_to?(:collect) kopy.send("#{association}=", self.send(association).collect {|i| i.clone(opts) }) else kopy.send("#{association}=", self.send(association).clone(opts))

    I'm novice in ruby so for sure it can be done lot nicer ;)

  • Will on 29 Dec 22:08

    "Plugin not found: ["git://github.com/DefV/deep_cloning.git"]"

    Good effort, though.

  • Will on 29 Dec 22:16

    Please disregard my last comment. I was able to install the plugin successfully -- must've been a network hiccup.

    Thanks for your effort!

  • Paddy on 28 Sep 19:13

    class ProductPage < ActiveRecord::Base
    has_many :blocks, :order => 'position', :dependent => :destroy end

    class Block < ActiveRecord::Base belongsto :productpage end

    class Bonus < Block hasone :featureitem, :foreignkey => 'featureid', :class_name => 'FeatureItem' end

    class Feature < Block hasmany :featureitems, :dependent => :destroy end

    class FeatureItem < ActiveRecord::Base end

  • Paddy on 28 Sep 19:16

    Ooops....Formatting Issue.

    http://pastie.org/633599

    How do I clone the FeatureItem?

    Please do let me know.

    Paddy

  • Daz on 16 Feb 07:36

    Pulled the plug?

  • djeryloare on 09 Sep 09:06

    unlock iphone 4 how to unlock iphone 4

    unlock iphone 4 how to unlock iphone 4 unlock iphone 4
    unlock iphone 4

    unlock iphone 4 unlock iphone 4 unlock iphone 4 how to unlock iphone 4

  • Post a comment

    Options:

    Size