Как чисто инициализировать атрибуты в Ruby с новым?

class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initalize params
    @name = params[:name]
    @age = params[:age]
    @email = params[:email]
    .
    .
    .
  end

Это кажется глупым способом сделать это. Что является лучшим/более идиоматическим способом инициализации объектов в Ruby?

Ruby 1.9.3

Ответ 1

def initialize(params)
  params.each do |key, value|
    instance_variable_set("@#{key}", value)
  end
end

Ответ 2

Вы можете просто перебирать ключи и вызывать сеттеры. Я предпочитаю это, потому что он поймает, если вы передадите недопустимый ключ.

class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initialize params = {}
    params.each { |key, value| send "#{key}=", value }
  end
end

foo = Foo.new name: 'Josh', age: 456
foo.name  # => "Josh"
foo.age   # => 456
foo.email # => nil

Ответ 3

Чтобы извлечь выгоду из ответа Джошуа Чика с небольшим обобщением

module Initializable
  def initialize(params = {})
    params.each do |key, value|
      setter = "#{key}="
      send(setter, value) if respond_to?(setter.to_sym, false)
    end
  end
end

class Foo
  include Initializable

  attr_accessor :name, :age, :email, :gender, :height
end

Foo.new name: 'Josh', age: 456
=> #<Foo:0x007fdeac02ecb0 @name="Josh", @age=456>

NB Если используется инициализационный микс, и нам нужна настраиваемая инициализация, мы просто вызываем super:

class Foo
  include Initializable

  attr_accessor :name, :age, :email, :gender, :height, :handler

  def initialize(*)
    super

    self.handler = "#{self.name} #{self.age}"
  end
end

Foo.new name: 'Josh', age: 45
=> #<Foo:0x007fe94c0446f0 @name="Josh", @age=45, @handler="Josh 45"> 

Ответ 4

Почему бы просто не указать явный список аргументов?

class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initialize(name, age, email, gender, height)
    @name = name
    @age = age
    @email = email
    @gender = gender
    @height = height
  end
end

Эта версия может содержать больше строк кода, чем другие, но она упрощает использование встроенных языковых функций (например, значения по умолчанию для аргументов или повышение ошибок, если initialize вызывается с неправильной арностью).

Ответ 5

Foo = Struct.new(:name, :age, :email, :gender, :height)

Этого достаточно для полноценного класса. Демо-ролик:

p Foo.class # Class

employee = Foo.new("smith", 29, "[email protected]", "m", 1.75) #create an instance
p employee.class # Foo
p employee.methods.sort # huge list which includes name, name=, age, age= etc

Ответ 6

Использование всех ключей из параметров неверно, вы можете определить неопытные имена. Я думаю, что это должен быть белый список имен

class Foo
   @@attributes = [:name, :age, :email, :gender, :height]  

   @@attributes.each do |attr|
     class_eval { attr_accessor "#{attr}" }
   end  

   def initialize params
     @@attributes.each do |attr|
       instance_variable_set("@#{attr}", params[attr]) if params[attr]
     end  
   end
end

Foo.new({:name => 'test'}).name #=> 'test'

Ответ 7

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

class Foo
  attr_reader :p
  def initalize p
    @p = p
  end
  def foo
    do_something_with(@p[:name])
    ...
  end
end

Если @p[:name] для вас слишком длинна, вы можете сохранить proc как переменную экземпляра и вызвать соответствующее значение, например @p.(:name).

class Foo
  attr_reader :p
  def initialize p
    @p = ->x{p[x]}
  end
  def foo
    do_something_with(@p.(:name))
    ...
  end
end

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

class Foo
  def initalize p
    @p = p
  end
  def get key
    @p[key]
  end
  def foo
    do_something_with(get(:name))
    ...
  end
end

Если вы хотите установить значения, вы можете определить метод сеттера и, если хотите, проверить недействительные ключи.

class Foo
  Keys = [:name, :age, :email, :gender, :height]
  def initalize p
    raise "Invalid key in argument" unless (p.keys - Keys).empty?
    @p = p
  end
  def set key, value
    raise "Invalid key" unless Keys.key?(key)
    @p[key] = value
  end
  def get key
    @p[key]
  end
  def foo
    do_something_with(get(:name))
    ...
  end
end