JvB
Photo

Jeroen van Baarsen

June 13, 2016
Ruby on rails dup vs clone

Disclaimer: This post is mostly a reminder for myself, so I dont have to Google it every time.

At my dayjob I sometimes have to copy database records, I know that rails has at least 2 (if you know more, please let me know!) built-in methods of doing so, but one does it slightly different than the other. The two methods I’m talking about are clone and dup.

The source of Rails is saying the following about both:

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.

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).

Ok, thats clear. But what does that exactly mean in practice? Lets create a code sample:

p1 = Post.create(title: 'Post 1', message: 'Amazing message')
=> #<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">

p2 = p1.clone
=> #<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 = p1.dup
=> #<Post id: nil, title: "Post 1", message: "Amazing message", created_at: nil, updated_at: nil>

As you can see, when you clone a DB object, it keeps all of its data, even the id, and when you use dup its creates a new object, with the data of the old object. The id and createdat and updatedat are empty though!

Lets take the code example above a little further, what happends if we change data

First the Clone

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

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

p1 #=> #<Post id: 1, title: "This is now P2", message: "Amazing message", created_at: "2014-07-01 19:45:44", updated_at: "2014-07-01 19:45:44">
p2 #=> #<Post id: 1, title: "This is now P2", message: "Amazing message", created_at: "2014-07-01 19:45:44", updated_at: "2014-07-01 19:45:44">

As you can see, when I change p2 the data in p1 is also changed. This is because clone only creates a Shallow Copy.

Now lets take a look at Dup

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>

The p3 object is only affected by our change, this is because its a new object, if you ask persisted?, it will return false, since it is not yet stored in the database. So if we want to save p3 we need to call save.

Does this also means that if I use clone that the objects are exactly the same? Lets check it out!

p1 = Post.create(title: 'Post 1', message: 'Amazing message')
p2 = p1.clone
p3 = p1.dup

p2 == p1 #=> true
p3 == p1 #=> false

So we can conclude that the clone method makes a shallow copy, and dup creates a new object. A little side note, dup does not copy associations