Ruby on Rails deep copy/ deep clone of object and its attributes

I would like to do a deep copy on objects including all the attributes.

The experiment_old is has 10 trials. And I want to copy everything to experiment_new with the 10 trials. experiment_old should also keep the 10 trial info.

However, all the cases I tried below, they copy everything well, but experiment_old does not have the 10 trials info. They just show up on experiment_new.

What is the best way to do deep copy for these cases.

case 1:

@experiment_new = Experiment.create(@experiment_old.attributes.merge(:trials => experiment_old.trails))

case 2:

@experiment_new = Marshal.load(Marshal.dump(@experiment_old.trials))

case 3:

@experiment_new = @experiment_old.clone

Here is the model:

class Experiment < ActiveRecord::Base belongs_to :experimenter has_many :trials has_many :participants
end
class Trial < ActiveRecord::Base belongs_to :experiment belongs_to :datum belongs_to :condition has_one :result_trial
end
2

2 Answers

You may enjoy the Amoeba gem for ActiveRecord 3.2.

It supports easy and automatic recursive duplication of has_one, has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.

be sure to check out the Amoeba Documentation but usage is pretty easy...

just

gem install amoeba

or add

gem 'amoeba'

to your Gemfile

then add the amoeba block to your model and run the dup method as usual

class Post < ActiveRecord::Base has_many :comments has_and_belongs_to_many :tags amoeba do enable end
end
class Comment < ActiveRecord::Base belongs_to :post
end
class Tag < ActiveRecord::Base has_and_belongs_to_many :posts
end
class PostsController < ActionController def some_method my_post = Post.find(params[:id]) new_post = my_post.dup new_post.save end
end

Your new post should have all the tags that were originally associated with it, and all the comments should be duplicated as well. You can disable the duplication of various records through the DSL, which you can read about in the documentation, but for example, if you wanted to keep the tags, but not the comments you could do something like this:

class Post < ActiveRecord::Base has_many :comments has_and_belongs_to_many :tags amoeba do include_field :comments end
end

or using the exclusive syntax

class Post < ActiveRecord::Base has_many :comments has_and_belongs_to_many :tags amoeba do exclude_field :comments end
end

or by specifying which field types to recognize (and thusly copy)

class Post < ActiveRecord::Base has_many :comments has_and_belongs_to_many :tags amoeba do recognize :has_and_belongs_to_many end
end

each of these various options should result in re-associating the new post with the same tags as the old post, but without duplicating the comments.

Amoeba will also automatically recurse in to child records if you enable them

class Post < ActiveRecord::Base has_many :comments amoeba do enable end
end
class Comment < ActiveRecord::Base belongs_to :post has_many :ratings amoeba do enable end
end
class Rating < ActiveRecord::Base belongs_to :comment
end

You can also prefix fields with some extra data to indicate uniqueness

class Post < ActiveRecord::Base has_many :comments amoeba do enable prepend :title => "Copy of " end
end

and in addition to prepend you can also append to or run a regex on a given field

Enjoy! :)

3

You should clone every trial and assign them to the cloned experiment:

@experiment_new = @experiment_old.clone
@experiment_old.trials.each do |trial| @experiment_new.trials << trial.clone
end

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like