ActiveRecord DeepClone Plugin

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
-
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
-
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
-
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
-
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 ;)
-
"Plugin not found: ["git://github.com/DefV/deep_cloning.git"]"
Good effort, though.
-
Please disregard my last comment. I was able to install the plugin successfully -- must've been a network hiccup.
Thanks for your effort!
-
class ProductPage < ActiveRecord::Base
has_many :blocks, :order => 'position', :dependent => :destroy endclass 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
-
Ooops....Formatting Issue.
http://pastie.org/633599
How do I clone the FeatureItem?
Please do let me know.
Paddy
-
Pulled the plug?
-
unlock iphone 4 how to unlock iphone 4
unlock iphone 4 how to unlock iphone 4 unlock iphone 4
unlock iphone 4unlock iphone 4 unlock iphone 4 unlock iphone 4 how to unlock iphone 4