How to cleanly initialize attributes in Ruby with new?

21,206

Solution 1

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

Solution 2

You can just iterate over the keys and invoke the setters. I prefer this, because it will catch if you pass an invalid key.

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

Solution 3

To capitalize on Joshua Cheek's answer with a bit of generalization

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 If the initialization mix-in has been used and we need custom initialization, we'd just call 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"> 

Solution 4

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

This is enough for a fully functioning class. Demo:

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

Solution 5

Why not just explicitly specify an actual list of arguments?

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

This version may be more lines of code than others, but it makes it easier to leverage built-in language features (e.g. default values for arguments or raising errors if initialize is called with incorrect arity).

Share:
21,206
B Seven
Author by

B Seven

Status: Hood Rails on HTTP/2: Rails HTTP/2 Rack Gems: Rack Crud Rack Routing Capybara Jasmine

Updated on May 19, 2020

Comments

  • B Seven
    B Seven almost 4 years
    class Foo
      attr_accessor :name, :age, :email, :gender, :height
    
      def initalize params
        @name = params[:name]
        @age = params[:age]
        @email = params[:email]
        .
        .
        .
      end
    

    This seems like a silly way of doing it. What is a better/more idiomatic way of initalizing objects in Ruby?

    Ruby 1.9.3