27111


## Rubyのcloneとdupの動き

### クラス定義

[code]

    class Hoge
      attr_accessor :name
    end
    
[/code]

### clone

[code]

    h = Hoge.new
    hc = h.clone
    
    h.name = 'name'
    hc.name = 'clone'
    h.name #=> 'name'
    
[/code]

### dup

[code]

    h = Hoge.new
    hd = h.dup
    
    h.name = 'name'
    hd.name = 'dup'
    h.name #=> 'name'
    
[/code]

## ActiveRecordのcloneとdupの動き

### clone

[code]

    p = Piyo.find(1)
    p.name = 'name'
    pc = p.clone
    pc.name = 'clone'
    p.name #=> 'clone'
    
[/code]

### dup

[code]

    p = Piyo.find(1)
    p.name = 'name'
    pd = p.dup
    pd.name = 'dup'
    p.name #=> 'name'
    
[/code]

## 挙動まとめ

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

## 参考資料

  * [ActiveRecord オブジェクトをコピーする – hello-world.jp.net](https://blog.hello-world.jp.net/ruby/809/)

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

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

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

>
[code]

>     # :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).
>  
[/code]

RoRのソースを見てみましたが、ドキュメント（コメント）はあるようですが、`clone`と`dup`の実装は見当たりませんでした。

## 質問

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


CC BY-SA 3.0
2016-06-27T03:46:26.363
27101
2016-07-01T02:42:01.600
2016-06-27T04:59:27.910
9008
9008
question
ruby-on-rails ruby rails-activerecord
RubyのcloneとdupとActiveRecordのcloneとdupは別物？


> RubyのcloneとdupとActiveRecordのcloneとdupは別物なのでしょうか？

同じものです。`Method#owner` でメソッドが定義されたクラスが得られますが、下記のいずれも `true` になります。

[code]

    Piyo.new.method(:clone).owner == Object.method(:clone).owner`
    Piyo.new.method(:dup).owner == Object.method(:dup).owner`
    
[/code]

ただし、`clone` の場合は `initialize_clone`、`dup` の場合は `initialize_dup` メソッドが呼ばれます
(参考: [initialize_clone, initialize_dup and initialize_copy in Ruby: Jon
Leighton](http://www.jonathanleighton.com/articles/2011/initialize_clone-
initialize_dup-and-initialize_copy-in-ruby/))。

ActiveRecord の場合、`ActiveRecord::Core#initialize_dup` が定義されていますので、`dup`
の挙動はここで変化しています。

> 違うとした場合、具体的にどのように違うのでしょうか？

## Object#dup, clone

Object の dup, clone はインスタンス変数をコピーしますが、これは浅いコピーであり、同じオブジェクトを参照します。

例では `h.name = "name"` とした時点で、`@name` が異なるオブジェクトを参照するので、`hc` に変更を加えても、影響がありません。

しかし、浅いコピーなので、同じオブジェクトを参照している状態で、そのオブジェクトに変更を加えると、両者に変更が及びます。

[code]

    h = Hoge.new
    h.name = "name"
    hc = h.clone
    hc.name.upcase!
    h.name #=> "NAME"
    
[/code]

## ActiveRecord::Base#clone

ActiveRecord::Base の clone は Object の clone と同じです。

ただし、ActiveRecord::Base では属性を 1 つの Hash に入れて、インスタンス変数に保持しています。

clone した場合は、同じ Hash を参照することになるので、属性の変更が clone 元にも及びます。上記の
**同じオブジェクトを参照している状態で、そのオブジェクトに変更を加える** ケースに該当します。

## ActiveRecord::Base#dup

ActiveRecord の dup は initialize_dup が定義されているので、そこに違いが現れます。

<https://github.com/rails/rails/blob/master/activerecord/lib/active_record/core.rb#L382>
を見ると、`@attributes.deep_dup` で属性の深いコピーを作成したり、主キーを nil にする処理が含まれています。

これにより、dup 元・dup 先は異なる Hash を参照することになるので、片方の変更がもう一方に影響しなくなります。

> If you need a copy of your attributes hash, please use the #dup
> method.と書いてあるのは深いコピーのことでしょうか？（Rubyのdupは浅いコピーなので別物になっている）

上記のように `dup` は `deep_dup` メソッドにより、属性の深いコピーが作成されます。  
`deep_dup` の実装は
<https://github.com/rails/rails/blob/efff6c1fd4b9e2e4c9f705a45879373cb34a5b0e/activesupport/lib/active_support/core_ext/object/deep_dup.rb>
です。


CC BY-SA 3.0
2016-06-27T10:16:45.753
27111
2016-07-01T02:42:01.600
2016-07-01T02:42:01.600
5789
5789
27101
answer