2

How do I clone a single attribute in a Rails model? This didn't work:

irb(main):309:0> u.reload
=> #<User id: 1, username: "starrychloe", ...
irb(main):310:0> u2 = u.dup
=> #<User id: nil, username: "starrychloe", ...
irb(main):311:0> u2 = u.clone
=> #<User id: 1, username: "starrychloe", ...
irb(main):312:0> u2.username = u.username.clone
=> "starrychloe"
irb(main):313:0> u2.username = 'star'
=> "star"
irb(main):314:0> u.username ############ Changes original
=> "star"

Neither did this:

irb(main):320:0> u.reload
=> #<User id: 1, username: "starrychloe", ...
irb(main):321:0> u2 = u.clone
=> #<User id: 1, username: "starrychloe", ...
irb(main):322:0> u2[:username] = u[:username].clone
=> "starrychloe"
irb(main):323:0> u2.username = 'cow'
=> "cow"
irb(main):324:0> u.username ############ Changes original
=> "cow"

#dup doesn't copy the ID, and #clone on the attribute keeps the reference to the same string. This will not solve my problem.

Community
  • 1
  • 1
Chloe
  • 25,162
  • 40
  • 190
  • 357
  • 1
    Using `dup` works and should not change your original instance attribute. I've tested this on Rails 4 and Ruby 2. `dup` does not copy the id as it initializes a new copy of the instance and therefore will not be the same object in the db. – Leo Correa Jan 27 '14 at 22:03

5 Answers5

2
u2 = User.new(u.attributes.merge(username: "cow"))

Also, take a look at this question. It has a lot of interesting info on similar subject: What is the easiest way to duplicate an activerecord record?

Community
  • 1
  • 1
Victor Ronin
  • 22,758
  • 18
  • 92
  • 184
1

Do you want to duplicate an instance or an attribute?

To duplicate an instance, use u2 = u.dup not u2 = u.clone.

Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70
1

You might wanna look into amoeba gem. https://github.com/rocksolidwebdesign/amoeba

BroiSatse
  • 44,031
  • 8
  • 61
  • 86
0

To make a copy of the instance with its attributes and de-reference you can do this:

u2 = u.class.new(u.attributes)
bjhaid
  • 9,592
  • 2
  • 37
  • 47
  • Peculiar, but it thinks it is a new object. Is there a way around that? `app.controller.view_context.form_for u2 do end`: `
    ...`.
    – Chloe Jan 27 '14 at 23:56
  • ? I'm not in the view. That was from the Rails Console. – Chloe Jan 28 '14 at 00:08
  • To edit. I do not want to display modified values outside of the edit form in case of validation errors, such as in the header or navigation panels. – Chloe Jan 28 '14 at 00:15
0

I ended up making copies of each of the fields I wanted to keep track of:

@oldUsername = @user.username.clone

User.new looked promising, but it treated the copy as a new object, when it was an existing model, and output invalid forms to edit the model in the views:

> app.controller.view_context.form_for u2 do end   # This is from Rails console
=> "<form accept-charset=\"UTF-8\" action=\"/users\" class=\"new_user\" id=\"new_user_1\" method=\"post\">

So it would attempt to PATCH to /users (from the view), which is invalid, when it should PATCH to /users/1/.

It's unbelievable that Rails won't clone objects correctly. In Java, you could use u2.setProperty( u.getProperty().clone() ) and be sure to have a new object that won't interfere with the old one.

Chloe
  • 25,162
  • 40
  • 190
  • 357