Gandalf StormCrow
Gandalf StormCrow

Reputation: 26212

Creating generic constructor in ruby

I found this interesting answer :

https://stackoverflow.com/a/2348854/169277

This is ok when you're trying to set instance variables it works really great.

Is there a way to apply the same logic or better one to create generic constructor like :

def initialize(obj)
  obj.each do |k,v|
   #find the setter for each k and set the value v to and return newly created object
  end
end

If I had object TestObject:

class TestObject
attr_accessor :name, :surname, :sex
end

I was thinking to create it something like this:

TestObject.new({:name => 'Joe', :surname => 'Satriani'})

How would one achieve this?

So doing this would be a shorthand of :

t = TestObject.new
t.name = 'Joe'
t.surname = 'Satriani'

Upvotes: 0

Views: 1738

Answers (3)

Jörg W Mittag
Jörg W Mittag

Reputation: 369458

I think it would be better to use keyword arguments for this. After all, the Hash keys are guaranteed to be valid Ruby identifier Symbols since they need to match up with method names. You don't need the capability to pass in arbitrary Ruby objects as keys of the Hash.

def initialize(**attrs)
  attrs.each do |attr, value| send(:"#{attr}=", value) end
end

TestObject.new(name: 'Joe', surname: 'Satriani')

Upvotes: 0

Chris Heald
Chris Heald

Reputation: 62648

Sure, you can use send to send arbitrary messages to an object. Since we're operating on self here, we can just invoke send directly.

def initialize(obj)
  obj.each do |k,v|
    send(:"#{k}=", v)
  end
end

For example, TestObject.new({:name => 'Joe'}) will call send "name=", "Joe".

Upvotes: 1

Logan Serman
Logan Serman

Reputation: 29880

You can inherit from Struct to make a simple object, and then pass in the attributes to the initializer:

class TestObject < Struct.new(:name, :surname, :sex)
end

TestObject.new('Joe', 'Satriani') #=> sex will be nil

You can use OpenStruct to make quick value objects with arbitrary attributes:

t = OpenStruct(name: 'Joe', surname: 'Satriani')

You can include a module like Virtus: https://github.com/solnic/virtus

Or you can do what Chris Heald said.

Upvotes: 1

Related Questions