Search code examples
rubyattributesstring-interpolation

Using interpolation within an attribute name (avoiding eval)


I'm defining my own method that will allow me to update multiple attributes of a given object to that of another object new_car. Many of the attributes have similar names such as "entered_text_1", "entered_text_2", "entered_text_3" up to "entered_text_10" (In the example below I've just done up to 3 to illustrate).

Problem

Wondering how to use interpolation within an attribute name itself, such as car.entered_text_"#{i}" (which is not correct)

Desired Outcome

The below code works and does what I'm looking for, however I've seen numerous warning about using eval - and I'm wondering what the better alternative is in this scenario?

# class Car and class NewCar exist with the same attribute names (:entered_text_1, :entered_text_2, :entered_text_3)

def self.copy_attributes(car, new_car)
  i = 1
  until i > 3
    eval("car.entered_text_#{i} = new_car.entered_text_#{i}")
    puts eval("car.entered_text_#{i}")
    i += 1
  end

end

current_car = Car.new('White', 'Huge', 'Slow')
golf = NewCar.new('Red', 'Small', 'Fast')

copy_attributes(current_car, golf)

# => Red, Small, Fast

Many thanks!


Solution

  • You could use the fact that assignments like user.name = 'John' are actually method calls and can be written like this: user.name=('John'), where name= is the name of the method. We can invoke methods dynamically with send (call any method) or public_send (call a public method, will raise error if method exists but is private).

    car.public_send("entered_text_#{i}=", new_car.public_send("entered_text_#{i}"))
    puts car.public_send("entered_text_#{i}")