I know how Forwardable#def_delegate works with methods on objects, but is there a similar way to forward methods names to hash keys. Like:
hash = { some_value: 42, other_value: 31415 }
def_delegate :hash, :some_value, :other_value
Calling object.some_value
should return 42
PS: def and class eval is a way, but is there a nicer way?
This is a good job for OpenStruct
, which basically wraps a Hash in an object.
2.2.1 :001 > require 'ostruct'
=> true
2.2.1 :002 > s = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
2.2.1 :003 > s.a
=> 1
2.2.1 :004 > s.c = 3
=> 3
If you want to be strict about the methods available, Struct
lets you create little, dynamic classes.
2.2.1 :001 > hash = {a: 1, b: 2}
=> {:a=>1, :b=>2}
2.2.1 :002 > struct = Struct.new(*hash.keys)
=> #<Class:0x007fd104b32888>
2.2.1 :003 > instance = struct.new(*hash.values)
=> #<struct a=1, b=2>
2.2.1 :004 > instance.a = 3
=> 3
2.2.1 :005 > instance.c
NoMethodError: undefined method `c' for #<struct a=3, b=2>
Not directly, no. One option is to use OpenStruct from Ruby's standard library.
require "ostruct"
class Foo
extend Forwardable
delegate :@data, :some_value, :other_value
def initialize(hash)
@data = OpenStruct.new(hash)
end
end
hash = { some_value: 42, other_value: 31415 }
foo = Foo.new(hash)
foo.some_value # => 42
A simpler option is to just delegate the :[]
method, but it's not as pretty:
class Foo
extend Forwardable
delegate :@data, :[]
def initialize(hash)
@data = hash
end
end
hash = { some_value: 42, other_value: 31415 }
foo = Foo.new(hash)
foo[:some_value] # => 42
Barring that, there's always define_method
:
[ :some_value, :other_value ].each do |meth|
define_method(meth) { @data[meth] }
end
Or method_missing
:
def method_missing(meth, *args, &block)
return @data[meth] if @data.key?(meth)
super
end
def respond_to_missing?(meth, *args)
@data.key?(meth) || super
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With