Ruby on Rails глубокое копирование / глубокое копирование объекта и его атрибутов - PullRequest
17 голосов
/ 17 июня 2011

Я хотел бы сделать глубокую копию объектов, включая все атрибуты.

Эксперимент_old имеет 10 испытаний. И я хочу скопировать все, чтобы экспериментировать - с 10 испытаниями. Эксперимент_old также должен хранить 10 пробную информацию.

Тем не менее, во всех случаях, которые я пробовал ниже, они все хорошо копируют, но Experiment_old не имеет информации о 10 испытаниях. Они просто появляются в эксперименте.

Какой лучший способ сделать глубокое копирование для этих случаев.

дело 1:

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

дело 2:

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

дело 3:

@experiment_new = @experiment_old.clone

Вот модель:

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 ]

24 голосов
/ 28 февраля 2012

Вам могут понравиться Amoeba gem для ActiveRecord 3.2.

Он поддерживает простое и автоматическое рекурсивное дублирование ассоциаций has_one, has_many и has_and_belongs_to_many, предварительную обработку полей иочень гибкая и мощная конфигурация DSL, которая может применяться как к модели, так и на лету.

обязательно ознакомьтесь с Документацией Amoeba , но ее использование довольно просто ...

просто

gem install amoeba

или добавьте

gem 'amoeba'

в ваш Gemfile

, затем добавьте блок amoeba в вашу модель и запустите метод dup как обычно

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

В вашем новом сообщении должны быть все теги, которые были изначально связаны с ним, и все комментарии также должны быть продублированы.Вы можете отключить дублирование различных записей через DSL, о котором вы можете прочитать в документации, но, например, если вы хотите сохранить теги, но не комментарии, вы можете сделать что-то вроде этого:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    include_field :comments
  end
end

или с использованием исключительного синтаксиса

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    exclude_field :comments
  end
end

или указав, какие типы полей нужно распознать (и, следовательно, скопировать)

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    recognize :has_and_belongs_to_many
  end
end

, каждая из этих различных опций должна привести к повторному ассоциированиюновый пост с теми же тегами, что и старый пост, но без дублирования комментариев.

Amoeba также автоматически вернется к дочерним записям, если вы включите их

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

Вы также можете добавить префиксполя с некоторыми дополнительными данными, указывающими уникальность

class Post < ActiveRecord::Base
  has_many :comments

  amoeba do
    enable
    prepend :title => "Copy of "
  end
end

, и в дополнение к предварительному заполнению вы также можете добавить или запустить регулярное выражение для данного поля

Наслаждайтесь!:)

22 голосов
/ 17 июня 2011

Вы должны клонировать каждое испытание и назначать их клонированному эксперименту:

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