【问题标题】:Using def_delegate with a hash将 def_delegate 与哈希一起使用
【发布时间】:2015-08-04 14:51:32
【问题描述】:

我知道 Forwardable#def_delegate 如何与对象上的方法一起工作,但是是否有类似的方法可以将方法名称转发到哈希键。喜欢:

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

调用object.some_value 应该返回42

PS:def 和 class eval 是一种方式,但还有更好的方式吗?

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    这对OpenStruct 来说是件好事,它基本上将一个哈希值包装在一个对象中。

    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
    

    如果您想严格控制可用的方法,Struct 可让您创建小的动态类。

    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>
    

    【讨论】:

      【解决方案2】:

      不直接,不。一种选择是使用 Ruby 标准库中的OpenStruct

      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
      

      一个更简单的选择是只委托:[] 方法,但它不是那么漂亮:

      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
      

      除此之外,总是有define_method

      [ :some_value, :other_value ].each do |meth|
        define_method(meth) { @data[meth] }
      end
      

      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
      

      【讨论】:

      • method_missing 选项是最好的,因为如果你调用一个存在的方法,OpenStruct 会返回 nil,这会导致各种麻烦。这应该是公认的答案。
      【解决方案3】:

      实现这一点的最佳方式可能不会涉及Forwardable,而是取决于您的具体用例。下面是一个不用 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
      

      【讨论】:

      • 是一个很好的解决方案我现在有类似的东西,但我一直在寻找更简单的方法,@Kristján 解决方案看起来不错
      • 我做了类似的事情,但基于 Rails delegate to: DSL,因为我认为它比 def_delegators - gist.github.com/eric-hemasystems/… 更直观
      猜你喜欢
      • 2019-10-05
      • 2011-07-12
      • 2018-07-03
      • 1970-01-01
      • 2012-11-19
      • 2011-01-19
      • 2011-11-10
      • 2017-02-28
      相关资源
      最近更新 更多