Rubyのcloneとdupの動き

クラス定義

class Hoge
  attr_accessor :name
end

clone

h = Hoge.new
hc = h.clone

h.name = 'name'
hc.name = 'clone'
h.name #=> 'name'

dup

h = Hoge.new
hd = h.dup

h.name = 'name'
hd.name = 'dup'
h.name #=> 'name'

ActiveRecordのcloneとdupの動き

clone

p = Piyo.find(1)
p.name = 'name'
pc = p.clone
pc.name = 'clone'
p.name #=> 'clone'

dup

p = Piyo.find(1)
p.name = 'name'
pd = p.dup
pd.name = 'dup'
p.name #=> 'name'

挙動まとめ

Rubyのcloneとdupはどちらもオリジナルのアトリビュートを変更しない。
ActiveRecordのcloneはオリジナルのアトリビュートを変更する。
ActiveRecordのdupはオリジナルのアトリビュートを変更しない。

参考資料

こちらの記事 を参考にさせていただきActiveRecord オブジェクトをコピーしようと思ったのですが、Rails3.1以降でdupとcloneの動作が逆になったようです。

RubyとARで違うような挙動な上に、途中で仕様が変わっているようで、混乱しています。私はRails4を想定しています。



* https://github.com/rails/rails/blob/master/activerecord/lib/active_record/core.rb#L356

# :method: clone
# Identical to Ruby's clone method.  This is a "shallow" copy.  Be warned that your attributes are not copied.
# That means that modifying attributes of the clone will modify the original, since they will both point to the
# same attributes hash. If you need a copy of your attributes hash, please use the #dup method.
#
#   user = User.first
#   new_user = user.clone
#   user.name               # => "Bob"
#   new_user.name = "Joe"
#   user.name               # => "Joe"
#
#   user.object_id == new_user.object_id            # => false
#   user.name.object_id == new_user.name.object_id  # => true
#
#   user.name.object_id == user.dup.name.object_id  # => false
##
# :method: dup
# Duped objects have no id assigned and are treated as new records. Note
# that this is a "shallow" copy as it copies the object's attributes
# only, not its associations. The extent of a "deep" copy is application
# specific and is therefore left to the application to implement according
# to its need.
# The dup method does not preserve the timestamps (created|updated)_(at|on).

RoRのソースを見てみましたが、ドキュメント(コメント)はあるようですが、clonedupの実装は見当たりませんでした。

質問

  • RubyのcloneとdupとActiveRecordのcloneとdupは別物なのでしょうか?
  • 違うとした場合、具体的にどのように違うのでしょうか?
  • If you need a copy of your attributes hash, please use the #dup method.と書いてあるのは深いコピーのことでしょうか?(Rubyのdupは浅いコピーなので別物になっている)