Ruby on rails Ruby:使用字符串设置哈希值

Ruby on rails Ruby:使用字符串设置哈希值,ruby-on-rails,ruby,hash,Ruby On Rails,Ruby,Hash,我正在使用Rails 4,并试图通过字符串名访问哈希变量 例如,假设我有一个策略模型,其中一个成员散列包含字段名称和地址 我希望能够将policy_member_name转换为policy[:member][:name] 但是,此字符串可能长于3个部分。我能够访问该值,但无法使用以下命令设置该值: ret = obj keys.each do |key| ret = ret[key.to_sym] end ret 其中keys是一个数组,比如['member','nam

我正在使用Rails 4,并试图通过字符串名访问哈希变量

例如,假设我有一个策略模型,其中一个成员散列包含字段名称和地址

我希望能够将policy_member_name转换为policy[:member][:name]

但是,此字符串可能长于3个部分。我能够访问该值,但无法使用以下命令设置该值:

  ret = obj
  keys.each do |key|
    ret = ret[key.to_sym]
  end
  ret
其中keys是一个数组,比如['member','name'],obj是对象,比如Policy.first。但是,此方法只返回策略[:member][:name]上的值,不允许我执行策略[:member][:name]=

谢谢你的帮助

编辑:


我希望能够调用
policy.member\u name
来获取
策略[:member][:name]
并拥有
策略。member\u name=“Something”
来设置
策略[:member][:name]=“Something”
这很容易用
方法实现

class HashSnake

  def initialize(hash)
    @hash = hash
  end

  def method_missing(method, *args, &block)
    parts = method.to_s.split('_').map(&:to_sym)
    pointer = @hash
    parts.each_with_index do |key, i|             
      if pointer.key? key
        if pointer[key].is_a? Hash
            if (i +1 == parts.length)
                return pointer[key]
            else
                pointer = pointer[key]
            end
        else
          return pointer[key]
        end
      # Checks if method is a setter
      elsif key[-1] == '=' && pointer.key?(key[0..-2].to_sym)
        pointer[key[0..-2].to_sym] = args[0]
      end
    end
  end
end

obj = HashSnake.new(
  member: {
    foo: 'bar',
    child: {
      boo: 'goo'
    }
  }
)

obj.member_foo = 'bax'
puts obj.member_foo.inspect
# "bax"
puts obj.member_child_boo.inspect
# goo

由于您只允许用户对某些散列进行读写访问,因此创建将这些散列(字符串)的名称映射到散列对象的散列不会有任何困难。例如:

h0 = { a: 1, b: 2 }
h1 = { c: 3, d: { e: 4, f: { g: 5 } } }

hash = { "h0"=>h0, "h1"=>h1 }
一旦完成,就只需要创建一种满足您需求的领域特定语言(“DSL”)。很可能有一些宝石可以做到开箱即用,或者可以根据您的要求进行修改

下面是一个可能性的例子。(我建议您在阅读代码之前先看一下示例。)

代码

class Array
  def h(hash)
    h = hash[first]
    if last.is_a? Array
      seth(h, *self[1..-2].map(&:to_sym), *last)
    else
      geth(h, *self[1..-1].map(&:to_sym))
    end
  end

  private

  def geth(h, *keys)
    keys.reduce(h) { |h,k| h[k] }
  end

  def seth(h, *keys, last_key, type, val)
    val =
      case type
      when "STR" then val.to_str
      when "INT" then val.to_int
      when "SYM" then val.to_sym
      when "FLT" then val.to_f
      end
    geth(h, *keys)[last_key] = val
  end
end
示例

以下内容适用于开头介绍的
h0
h1
hash

["h0", "b"].h(hash)
   #=> 2
["h1", "d", "f", "g"].h(hash)
   #=> 5

["h0", "b", ["INT", 3]].h(hash)
   #=> 3
h0 #=> {:a=>1, :b=>3}
["h1", "d", "f", "g", ["STR", "dog"]].h(hash)
   #=> "dog"
h1 #=> {:c=>3, :d=>{:e=>4, :f=>{:g=>"dog"}}, :g=>"dog"} 

你能给我们已知的输入和期望的输出吗?旁注:在我看来,你是在为一些可能没有你想象的那么重要的事情牺牲性能。即使是非常接近您想要的,也会遭受性能代价,因为它依赖于
方法\u missing
。。。此外,它可能更容易混淆,而不是有用——例如,当:
policy.member=Users.find(1)时会发生什么;policy.member_name='Joe';放置policy.member
…?我需要它的原因是我正在使用gem
best\u in\u place
,以便在适当的位置编辑字段。您是建议根本不使用
OpenStructs
,还是仅仅建议不需要点符号?或者您是否知道另一种就地编辑
OpenStructs
的方法?谢谢@Myst的评论总结得很好。你真的需要这个吗?在性能方面有相当大的成本,我甚至不想考虑堆栈跟踪是什么样子。