【问题标题】:Hash keys as accessors in a class散列键作为类中的访问器
【发布时间】:2011-08-11 11:45:23
【问题描述】:

我正在研究一个读取一些传感器信息并将其作为哈希返回的类。我想使用哈希键作为访问器,但我没有太多运气让它工作。到目前为止,这是我的代码的相关部分:

我已经尝试过使用 method_missing 和 :define_method 方法。

  attr_reader :sensor_hash

  def method_missing(name, *args, &blk)
    if args.empty? && blk.nil? && @sensor_hash.has_key?(name.to_s)
      @sensor_hash[name.to_s]
    else
      super
    end
  end

  def sensor(*sensor_to_return)
    sensor_output = run_command(this_method_name)
    sensor_output = sensor_output.split("\n")
    sensor_output.map! { |line| line.downcase! }
    unless sensor_to_return.empty?
      sensor_to_return = sensor_to_return.to_s.downcase
      sensor_output = sensor_output.grep(/^#{sensor_to_return}\s/)
    end
    @sensor_hash = Hash.new
    sensor_output.each { |stat| @sensor_hash[stat.split(/\s+\|\s?/)[0].gsub(' ','_').to_sym] = stat.split(/\s?\|\s?/)[1..-1].each { |v| v.strip! } }
    @sensor_hash.each do |k,v|
      puts v.join("\t")
      self.class.send :define_method, k { v.join("\t") }
    end
    return @sensor_hash

返回的数据是以传感器名称为键的散列,值是返回的所有其他内容的数组。我的目标是能够调用Class.sensor.sensor_name 并获得Class.sensor[:sensor_name] 的输出。目前,我所能得到的只是一个未定义的方法错误。有人知道我在这里做错了什么吗?

【问题讨论】:

    标签: ruby methods hash accessor


    【解决方案1】:

    也许 OpenStruct 可以满足您的需求。来自doc:“它就像一个哈希,以不同的方式访问数据。实际上它是用哈希实现的,你可以用一个来初始化它。”

    require 'ostruct'
    s=OpenStruct.new({:sensor_name=>'sensor1',:data=>['something',1,[1,2,3]]})
    p s.sensor_name
    #=> "sensor1"
    

    【讨论】:

      【解决方案2】:

      只是一个简单的例子。你有什么理由不给你的 Hash 做猴子补丁吗?

      irb(main):001:0> class Hash
      irb(main):002:1> def method_missing(name, *args, &blk)
      irb(main):003:2>   if self.keys.map(&:to_sym).include? name.to_sym
      irb(main):004:3>     return self[name.to_sym]
      irb(main):005:3>   else
      irb(main):006:3*     super
      irb(main):007:3>   end
      irb(main):008:2> end
      irb(main):009:1> end
      => nil
      irb(main):012:0> h = {:hello => 'world'}
      => {:hello=>"world"}
      irb(main):013:0> h.hello
      => "world"
      

      【讨论】:

      • 这正是我想要的。我没有意识到我需要将 method_missing 方法放在 Hash 类中。成功了。
      【解决方案3】:

      您可以使用缺少方法的包装类,这样您就不必修改Hash

      class AccessibleHash
        def initialize(hash)
          @hash = hash
        end
      
        def method_missing(name, *args, &block)
          sname = name.to_sym
          if @hash.keys.include? sname
            return @hash[sname]
          else
            super
          end
        end
      end
      

      或者,如果您正在使用 Rails,它有一些不错的内置对象委托SimpleDelegator。这将允许您在哈希以及其中的任何嵌套哈希上定义访问器。

      class AccessibleHash < SimpleDelegator
        def initialize
          define_accessors(self.keys)
        end
      
        def define_accessors(keys)
          keys.each do |key|
            defind_accessors(body[key].keys)
            self.define_singleton_method(key) { self[key] }
          end
        end
      end
      
      ah = AccessibleHash.new({ some: 'hash', with: { recursive: 'accessors' })
      ah.with.recursive == 'accessors'
      => true
      

      这在实例化时的性能将低于method_missing,因为它必须在创建后立即在您的委托对象上递归运行。但是,它绝对比method_missing 更安全,而且肯定比猴子修补Hash 课程更安全。当然,安全性与您的目标有关,如果您的应用程序只做这些,那么猴子补丁就会消失。

      如果你想要没有rails的递归嵌套访问器,你可以结合上面的方法来做这样的事情......

      class AccessibleHash
        def initialize(hash)
          @hash = hash
          define_accessors(@hash.keys)
        end
      
        def define_accessors(keys)
          keys.each do |key|
            @hash[key] = self.class.new(@hash[key]) if @hash.keys.present?
      
            self.define_singleton_method(key) { self[key] }
          end
        end
      end
      

      但是在这一点上,您变得非常疯狂,可能值得重新评估您的解决方案以支持更面向对象的东西。如果我在代码审查中看到任何这些,肯定会引发危险信号。 ;)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-12
        • 2023-03-23
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 2014-09-05
        • 2018-04-27
        • 2012-10-27
        相关资源
        最近更新 更多