DRY способ присвоить хэш-значения объекту

Я ищу элегантный способ присвоить значение, хранящееся внутри Hash, в ранее существовавшем объекте. Чтобы быть ясным, если у меня есть объект, скажите obj с двумя атрибутами, скажем, именем и возрастом, я хочу присвоить эти значения, исходящие из хэша, без чего-то вроде:

obj.name = hash[:name]
obj.age = hash[:age] 

Спасибо за внимание. Simone

Ответ 1

Лучшая ставка - это, вероятно, просто определить метод типа update_attributes, который принимает хэш и делает это внутри метода экземпляра класса.

Расширяя то, что написано другими, и что вам кажется, я думаю, что ваш лучший выбор был бы следующим:

hash.keys.each do |key|
  m = "#{key}="
  obj.send( m, hash[key] ) if obj.respond_to?( m )
end

Это будет учитывать:

  • не все атрибуты класса в хеше, и
  • любое количество ключей в хэше (не только: имя и т.д.)

Ответ 2

Вот довольно простой в использовании шаблон, который я использую в течение многих лет:

{
  first_name: "John",
  last_name: "Smith",
  ...
}.each {|k, v| send("#{k}=", v)}

Ответ 3

Не знаю об элегантности, но вы можете сделать:

[:name, :age].each do |att|
  obj.send("#{att}=", hash[att])
end

Ответ 4

Использование instance_variable_set - это еще один вариант. Вот пример:

class Test; attr_accessor :name, :age; end

hash = {name: 'John', age: 52}

obj = Test.new
hash.each do |k,v|
  obj.instance_variable_set("@#{k}".to_sym, v) # :name is converted to :@name
end

p obj # => #<Test:0x007fd8cbb75b00 @name="John", @age=52> 

Единственный трюк в том, что instance_variable_set ожидает символ, начинающийся с @, поэтому :@name действителен, но :name не является.

Ответ 5

obj.methods.grep(/[^!=]=$/).each {|attr| obj.send(attr, hash[attr]) }

Ответ 6

Вы можете напрямую вызвать метод assign_attributes для объекта.

obj.assign_attributes(hash)