我正在尝试编写一个将接受哈希作为参数的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
但我没有看到用当前绑定以外的任何东西创建新块的方法.如何使用给定的绑定创建块?
最佳答案 也许我错过了一些东西,但我认为你正在尝试使用非常复杂的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
当然它缺少别名,但这不是重点.
编辑:
在创建具有自定义绑定的块时,答案是:否.但是,您可以使用不同的绑定调用代码块,或者使用绑定方法捕获,或者仅通过提供与其绑定相关的对象.
您可以为此目的使用eval(它接受Binding对象作为第二个参数)或更好的instance_eval,class_eval,instance_exec和class_exec.你可以在Jay Fields’ Thoughts blog entry开始挖掘.