web-dev-qa-db-ja.com

Rubyの属性をnewできれいに初期化する方法は?

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

29
B Seven
def initialize(params)
  params.each do |key, value|
    instance_variable_set("@#{key}", value)
  end
end
17
Zach Kemp

キーを繰り返し処理して、セッターを呼び出すだけです。これは、無効なキーを渡したときにキャッチされるため、こちらをお勧めします。

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
41
Joshua Cheek

ジョシュアチークの回答を少し一般化して活用する

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"> 
12
Vadym Tyemirov
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
steenslag

引数の実際のリストを明示的に指定しないのはなぜですか?

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が誤ったアリティで呼び出された場合にエラーが発生します)。

4
pje

Paramsのすべてのキーを使用することは正しくありません。不本意な名前を定義できます。名前のホワイトリストだと思う

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'
3
megas

唯一の引数としてハッシュを受け取っている場合、それをインスタンス変数として保持しないのはなぜですか?値が必要なときはいつでも、ハッシュから呼び出します。インスタンス変数名を短くして、簡単に呼び出せるようにすることができます。

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

_@p[:name]_がまだ長すぎる場合は、プロシージャをインスタンス変数として保存し、@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
_

値を設定する場合は、setterメソッドを定義し、必要に応じて無効なキーをさらにチェックできます。

_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
_
1
sawa