11

I need to know the difference between Rails dup and clone methods, because dup duplicates the id attribute and clone doesn't:

juan:~/alhambra$ rails c
Loading development environment (Rails 3.0.1)
1.9.3-p551 :001 > @user=User.last
 => #<User id: 2, email: "ferbad12@hotmail.com", encrypted_password: "$2a$10$/Fsz8DZ9PQbReTU1.wyxS.m3IOxZSV3siKDrrtUJdupz...", password_salt: "$2a$10$/Fsz8DZ9PQbReTU1.wyxS.", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2015-05-06 23:34:20", last_sign_in_at: "2015-05-06 23:34:20", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2015-05-06 23:33:37", updated_at: "2015-05-06 23:34:20"> 
1.9.3-p551 :002 > @user.clone
 => #<User id: nil, email: "ferbad12@hotmail.com", encrypted_password: "$2a$10$/Fsz8DZ9PQbReTU1.wyxS.m3IOxZSV3siKDrrtUJdupz...", password_salt: "$2a$10$/Fsz8DZ9PQbReTU1.wyxS.", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2015-05-06 23:34:20", last_sign_in_at: "2015-05-06 23:34:20", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2015-05-06 23:33:37", updated_at: "2015-05-06 23:34:20"> 
1.9.3-p551 :003 > @user.dup
 => #<User id: 2, email: "ferbad12@hotmail.com", encrypted_password: "$2a$10$/Fsz8DZ9PQbReTU1.wyxS.m3IOxZSV3siKDrrtUJdupz...", password_salt: "$2a$10$/Fsz8DZ9PQbReTU1.wyxS.", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2015-05-06 23:34:20", last_sign_in_at: "2015-05-06 23:34:20", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2015-05-06 23:33:37", updated_at: "2015-05-06 23:34:20"> 
alfakini
  • 4,635
  • 2
  • 26
  • 35
ruby student
  • 1,059
  • 4
  • 12
  • 18
  • 1
    Good blog post on it: http://www.jvanbaarsen.com/blog/2014/07/01/ruby-on-rails-dup-vs-clone ; tl;dr: `clone` will make an ActiveRecord object whose changes affect the original; `dup` makes a new ActiveRecord object (without `id`). – Amadan May 07 '15 at 01:03
  • @numbers1311407: [The answer by jvalanen](http://stackoverflow.com/a/24650062/240443) is the only relevant one in that thread, as most of the other answers, as well as the question, are about Ruby, not Rails/AR. – Amadan May 07 '15 at 01:12
  • Guys that is happening with my console. Dup is copying the id and clone is doing a new copy of id – ruby student May 07 '15 at 01:28
  • 1
    As noted by @Amadan this wasn't a duplicate, but it's also not just a question specific to the Rails implementations of `dup` & `clone`. It asks why `dup` and `clone` aren't giving expected results, and it turns out that between Rails 3 and 4 the method roles *completely reversed*. This is probably a common enough cause of confusion to warrant the existence of the question, if it were worded a little differently. – numbers1311407 May 07 '15 at 13:23

2 Answers2

18

In rails 3.0, dup and clone performed essentially opposite roles as to what they do now. From ActiveRecord::Base:

Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone as it copies the object's attributes only, not its associations. The extent of a "deep" clone is application specific and is therefore left to the application to implement according to its need.

While it can be seen in the same file that dup simple copied the record and its attributes:

  def dup
    obj = super
    obj.instance_variable_set('@attributes', @attributes.dup)
    obj
  end

This differs from current rails 4, which defines dup and clone to more follow the note from the ruby docs, noted in a similar question not specific to rails.

In general, clone and dup may have different semantics in descendent classes. While clone is used to duplicate an object, including its internal state, dup typically uses the class of the descendent object to create the new instance.

As can be seen from the from the more current ActiveRecord source:

##
# :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).
Community
  • 1
  • 1
numbers1311407
  • 33,686
  • 9
  • 90
  • 92
2

The tutorial explains this, everything opposite what my console displays

p1 = Post.create(title: 'Post 1', message: 'Amazing message')

p3 = p1.clone
p3.title = "This is now p3"

p1 #=> #<Post id: 1, title: "Post 1", message: "Amazing message", created_at: "2014-07-01 19:45:44", updated_at: "2014-07-01 19:45:44">
p3 #=> #<Post id: nil, title: "This is now P3", message: "Amazing message", created_at: nil, updated_at: nil>
ruby student
  • 1,059
  • 4
  • 12
  • 18