创建一个绑定,其唯一变量是来自哈希的键

时间:2014-04-06 20:22:05

标签: ruby

我知道我可以使用ostruct从哈希创建绑定,如下所示:

require 'ostruct'

not_in_locals = "we don't want this"
locals = {a: 1, b: 2}
my_binding = OpenStruct.new(locals).instance_eval { binding }
p my_binding
p eval("a", my_binding) #good, we want this
p eval("b", my_binding) #good, we want this
p eval("not_in_locals", my_binding) #bad, don't want it to access this, but it can

您可以在此处查看输出,该输出确认代码中的注释:https://eval.in/132925

正如您所看到的,问题是Binding还绑定了Hash中 not 的本地上下文中的变量。我想要一个从Hash创建Binding对象的方法,它除了来自Hash的键之外什么都没有绑定。

2 个答案:

答案 0 :(得分:1)

你可以尝试这样的事情。它绕过了一个直接的eval()调用,用于辅助类方法调用。 helper类方法名为evaluate(),但它只适用于非常简单的值(目前只测试过的字符串和整数)并且依赖于inspect()。

但是,如果您正在处理的值的类型是提前知道的,则可以将其修改为有效。

class HashBinding
  def initialize(hash)
    @hash = hash
  end

  def evaluate(str)
    binding_code = @hash.to_a.map do |k,v|
      "#{k} = #{v.inspect};"
    end.join(" ")
    eval "#{binding_code}; #{str}"
  end
end

not_in_hash = 'I am not in the hash'
hash = { :foo => 'foo value', :baz => 42}
hash_binding = HashBinding.new(hash)

hash_binding.evaluate('puts "foo = #{foo}"')
hash_binding.evaluate('puts "baz = #{baz}"')
hash_binding.evaluate('puts "not_in_hash = #{not_in_hash}"')

输出结果为:

ruby binding_of_hash.rb
foo = foo value
baz = 42
binding_of_hash.rb:10:in `eval': undefined local variable or method `not_in_hash' for #<HashBinding:0x007fcc0b9ec1e8> (NameError)
    from binding_of_hash.rb:10:in `eval'
    from binding_of_hash.rb:10:in `evaluate'
    from binding_of_hash.rb:20:in `<main>'

答案 1 :(得分:0)

这是另一个“不那么”的hacky尝试,它不依赖于“检查”序列化。

require 'json'

class HashBinding
  attr_accessor :hash
  def initialize(hash)
    # Require symbol keys
    @hash = Hash[hash.map {|k,v| [k.to_sym, v]}]
  end

  def evaluate(str)
    binding_code = @hash.to_a.map do |k,v|
      "#{k} = @hash[:#{k}];"
    end.join(" ")
    eval "#{binding_code}; #{str}"
  end
end

not_in_hash = 'I am not in the hash'
hash = { :foo => 'foo value', :baz => 42, 'str' => "a string value"}
hash_binding = HashBinding.new(hash)

hash_binding.evaluate('puts "foo = #{foo}"')
hash_binding.evaluate('puts "baz = #{baz}"')
hash_binding.evaluate('puts "str = #{str}"')
hash_binding.evaluate('puts "not_in_hash = #{not_in_hash}"')