Using def_delegate with a hash

I know how Forwardable # def_delegate works with methods on objects, but is there a similar way to redirect method names to hash keys. How:

hash = { some_value: 42, other_value: 31415 } def_delegate :hash, :some_value, :other_value 

The call to object.some_value should return 42

PS: def and class eval is a way, but is there a better way?

+4
source share
3 answers

This is a good job for OpenStruct , which basically wraps the hash in the 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 > sa => 1 2.2.1 :004 > sc = 3 => 3 

If you want to be strict about the methods available, Struct allows you to create small 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> 
+5
source

Not directly, no. One option is to use OpenStruct from the Ruby 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 simply delegate the method :[] , but this is not so 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 

If this is not the case, 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 
+2
source

The best way to implement this will probably not include Forwardable and will depend on your specific use case. Here is an example of one way to do this without eval:

 class C class << self attr_accessor :hash def def_hash_delegate(key) define_method(key) do C.hash[key] end end end @hash = { some_value: 42, other_value: 31415 } def_hash_delegate :some_value def_hash_delegate :other_value def_hash_delegate :value_3 end c = C.new puts c.some_value puts c.other_value C.hash[:value_3] = 3948 puts c.value_3 
+1
source

All Articles