Railsでのオブジェクトのコピー

Railsでのオブジェクトのコピー

みなさんこんにちは。

意識高い系エンジニアの僕です。

先日Railsでモデルをコピーした時にどうなるかを色々聞かれたので、Railsでのオブジェクトのコピーについて少しまとめてみました。

対象バージョン

  • Ruby 2.2.2
  • Rails 4.2.3

Railsのdeep_dupメソッド

Rubyでオブジェクトをコピーする場合、Object#dupObject#cloneが使えますが、
dupcloneではシャローコピー(浅いコピー)のため、オブジェクトの参照先までは
コピーされません。

オブジェクトの参照先までコピーする場合、RubyではMarshal.dumpMarshal.load
を使うことで実現できますが、Railsでは*HashやArrayに関してはdeep_dupメソッドが用意されてるので、
Marshalを使わなくてもディープコピーができます

[1] pry(main)> dog1 = { name: 'pochi', age: 10 }
=> {:name=>"pochi", :age=>10}
[2] pry(main)> dog2 = dog1.deep_dup
=> {:name=>"pochi", :age=>10}
[3] pry(main)> dog1[:name].object_id
=> 70164419145840
[4] pry(main)> dog2[:name].object_id
=> 70164454968820

ActiveRecordに対するdupとclone

ActiveRecordオブジェクトに対するdupとcloneは以下のようになります。

dup

  • idはnilになる
  • 新しいレコードになり、new_record?trueとなる
  • 保存しないと永続化しない
  • 新規レコードなので保存をするとbefore_save,before_createが呼ばれる
  • idがnilになるため、idをキーにしたhas_manyのアソシエーションなどは外れる

clone

  • idはコピー元と同じなる
  • 既存レコードになり、new_record?falseとなる
  • 既存レコードなので保存をするとbefore_save,before_updateが呼ばれる
  • アソシエーションはそのまま維持される
  • 複製元とはobject_idが違う別インスタンスですが、複製元の値を変更すると複製先の値も変わる(saveする前でも変わる)

レコードを複製する場合はdupを使う必要があります。
dupsaveをしないと永続化されませんが、saveをした場合にコールバックが呼ばれることによって想定外のことが起こらないように注意する必要はあると思います。

TAG

  • このエントリーをはてなブックマークに追加
金子 将範
エンジニア 金子 将範 rubyist

新しいことや難しい課題に挑戦することにやりがいを感じ、安定やぬるい事は退屈だと感じます。 考えるより先に手が動く、肉体派エンジニアで座右の銘は諸行無常。 大事なのは感性、プログラミングにおいても感覚で理解し、感覚で書きます。