如何在ruby中创建具有给定绑定的块?

时间:2011-07-08 09:00:48

标签: ruby binding block

我正在尝试编写一个assert_difference版本,它接受一个哈希作为参数,这样就不用写

assert_difference 'thing1', 1 do
  assert_difference ['thing2a', 'thing2b'], 2 do
    assert_difference 'thing3', -3 do
      # some triple-indented code
    end
  end
end

我可以写

assert_difference 'thing1' => 1, ['thing2a', 'thing2b'] => 2, 'thing3' => 3 do
  # some single-indented code
end

我到目前为止

def assert_difference_with_hash_support(expression, difference = 1, message = nil, &block)
  if expression.is_a? Hash
    expression.each do |expr, diff|
      block = lambda do
        assert_difference_without_hash_support expr, diff, &block
      end
    end
    block.call
  else
    assert_difference_without_hash_support(expression, difference, message, &block)
  end
end
alias_method_chain :assert_difference, :hash_support

但这不起作用,因为assert_difference在评估表达式时使用了块的绑定。我想要做的是创建一个带有原始绑定的新块 - 类似于:

    b = block.send :binding
    expression.each do |expr, diff|
      block = lambda(b) do
        assert_difference_without_hash_support expr, diff, &block
      end
    end
    block.call

但我还没有看到使用当前绑定以外的任何方式创建新块的方法。如何使用给定的绑定创建块?

1 个答案:

答案 0 :(得分:2)

也许我错过了一些东西,但我认为你正在尝试使用非常复杂的ruby功能,而它们对于解决你的问题是不必要的。

我的解决方案是:

def assert_hash(hash, &block)
  if hash.length > 1
    assert_difference(*hash.shift) do
      assert_hash(hash, &block)
    end
  else
    assert_difference(*hash.first, &block)
  end
end

当然它缺少别名,但这不是重点。

编辑:

在使用自定义绑定创建块时,答案是:否。但是你可以用不同的绑定调用代码块,或者用binding方法捕获,或者只提供与它有关联的对象。

您可以为此目的使用eval(它接受Binding对象作为第二个参数)或更好instance_evalclass_evalinstance_execclass_exec 。您可以在Jay Fields' Thoughts blog entry开始挖掘。