基于哈希更新的更新属性

时间:2017-04-18 20:06:51

标签: ruby

我有一个项目,我在一个类上动态创建属性,这些属性也作为哈希存储在另一个属性中,该属性用作所有这些属性及其值的集合。我希望能够做的是在更新哈希时更新属性,反之亦然。

这样的事情:

class SomeClass


  def initialize
    #code that creates the attributes on the class
    config_list.each { |config| self.class.send(:attr_accessor, config) }
  end

  #list of attributes that are dynamically added to the class
  #this is normally read from a config file but I added it here to simplify
  def config_list
    [:abc, :def, :ghi]
  end

  def configuration
    config_list.inject({}) do |r,e| 
      r[e] = instance_variable_get("@#{e.to_s}"); r 
    end
  end
end

用法:

x = SomeClass.new
  #=> #<SomeClass:0x007f9931990710>
x.abc = 5
  #=> 5
x.configuration
  #=> {:abc=>5, :def=>nil, :ghi=>nil}
x.configuration[:abc] = 10
  #=> 10
x.abc
  #=> 5

我想要最后一次电话会议:

x.abc

返回10,因为配置的值已更新。那可能吗?如果是这样,我该怎么做?

1 个答案:

答案 0 :(得分:1)

以下是我认为你想要实现的一个扩展示例。

我为Configuration创建了一个类来管理预定义的键,这样就无法操作哈希中的键。我只授予其[][]=访问权限,并为to_h添加了Hash,但却欺骗了@configuration,因此您无法通过其中操纵它。

class SomeClass
  # build this list in anyway you see fit 
  PREDEFINED_KEYS =  [:abc, :def, :ghi] 
  # define getter/setter methods for the PREDEFINED_KEYS
  PREDEFINED_KEYS.each do |m|
    define_method(m) do 
      @configuration[m] 
    end
    define_method("#{m}=") do |val|
      @configuration[m] = val
    end
  end 
  # intialize a new Configuration with the PREDEFINED_KEYS
  def initialize
    @configuration = Configuration.new(PREDEFINED_KEYS)
  end
  # alternate implementation using initialize instead of a constant
  # def initialize(keys)
  #   @configuration = Configuration.new(keys)
  #   instance_eval do 
  #     keys.each do |m|
  #       define_singleton_method(m) do 
  #         @configuration[m] 
  #       end
  #       define_singleton_method("#{m}=") do |val|
  #         @configuration[m] = val
  #       end
  #     end
  #   end
  # end
  # Usage: SomeClass.new([:abc,:def,:ghi])

  # accept a block and return the configuration class
  def configuration
    yield @configuration if block_given? 
    @configuration 
  end
  # convert to a proper Hash
  def to_h
    @configuration.to_h
  end

  class Configuration
    class UndefinedKeyError < StandardError;end
    # start out all keys with a value of nil
    def initialize(keys)
      @configuration = Hash[keys.product([nil])]
    end
    # retrieve value by key just like a normal Hash
    def [](k)
      @configuration[k]
    end
    # set value by key like a normal Hash 
    # excepting the key must be one of the keys defined in initialization
    def []=(k,v)
      raise(UndefinedKeyError, "must be one of #{@configuration.keys}") unless @configuration.has_key?(k)
      @configuration[k] = v
    end
    def to_h
      @configuration.dup
    end
  end
end

然后您的用例应该正常工作。您所要做的就是从文件中读出密钥,而不是静态定义的PREDEFINED_KEYS

用法

s = SomeClass.new
s.abc = 12
s.to_h
#=> {abc: 12, def: nil, ghi: nil}
s.configuration[:def] = 19
s.to_h
#=> {abc: 12, def: 19, ghi: nil}
s.configuration do |c|
  c[:ghi] = 22
end.to_h
#=> {abc: 12, def: 19, ghi: 22}
s.ghi
#=> 22
s.configuration[:rbc] = 19
#=> SomeClass::Configuration::UndefinedKeyError: must be one of [:abc, :def, :ghi]
s.configuration[:lmn]
#=> nil