开发者

Hash keys as accessors in a class

开发者 https://www.devze.com 2023-02-28 19:57 出处:网络
I\'m working on a class that reads some sensor information and returns it as a hash.I would like to use the hash keys as accessors, but I\'m not having much luck getting it work.Here are the relevant

I'm working on a class that reads some sensor information and returns it as a hash. I would like to use the hash keys as accessors, but I'm not having much luck getting it work. Here are the relevant parts of my code so far:

I've tried it both with method_missing and by using the :define_method 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(/^#{se开发者_如何转开发nsor_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

The data returned is a hash with the sensor name as the key and and the value is an array of everything else returned. My goal is to be able to call Class.sensor.sensor_name and get the output of Class.sensor[:sensor_name]. Currently, all I'm able to get is an undefined method error. Anybody have any idea what I'm doing wrong here?


Maybe OpenStruct does what you want. From the doc :"It is like a hash with a different way to access the data. In fact, it is implemented with a hash, and you can initialize it with one."

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


Just a quick example. Do you have any reasons to not monkey-patch your 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"


You could use a wrapper class with method missing so you don't have to monkey patch 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

Or, if you are working with Rails it has some nice built in object delegation with SimpleDelegator. That would allow you to define accessors on your hash as well as any nested hashes within it.

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

This would be less performant at instantiation than method_missing, because it has to run recursively over your delegatee object as soon as it's created. However, it's definitely safer than method_missing, and certainly way safer than monkey patching your Hash class. Of course, safety is relative to your goals, If it's all your application does then monkey patch away.

And if you want the recursive, nested accessors without rails you could do something like this with a combination of the above...

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

But at that point you're getting pretty crazy and it's probably worth reevaluating your solution in favor of something more Object Oriented. If I saw any of these in a code review it would definitely throw up a red flag. ;)

0

精彩评论

暂无评论...
验证码 换一张
取 消