哈希键作为类中的访问器

时间:2011-04-23 13:35:56

标签: ruby methods hash accessor

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

我已经使用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]的输出。目前,我能够获得的是一个未定义的方法错误。任何人都知道我在这里做错了什么?

3 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

只是一个简单的例子。你有什么理由不修补你的哈希?

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"

答案 2 :(得分:0)

您可以使用缺少方法的包装器类,这样就不必猴子修补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

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