在 Ruby 中的方法调用中从块范围访问变量

Access variable from the block's scope inside a method call in Ruby

我正在尝试创建一个类似于 Rails assert_difference 的 minitest 断言,但它会检查任何差异,而不仅仅是数字差异。

这是我当前的实现:

Minitest::Assertions.module_eval do

  def assert_changed(expression, &block)
    unless expression.respond_to?(:call)
      expression = lambda{ eval(expression, block.binding) }
    end
    old = expression.call
    block.call
    refute_equal old, expression.call
  end

end

现在在我的测试套件中,我通过执行以下操作来调用它:

# this is working 
username = 'Jim'
assert_changed lambda{ username } do
  username = 'Bob'
end

# this is not working
username = 'Jim'
assert_changed 'username' do
  username = 'Bob'
end

第一次使用 lambda(或 proc)调用 assert_changed 工作得很好。使用带有变量名称的字符串的第二次调用不起作用。

当它到达这一行时:expression = lambda{ eval(expression, block.binding) } 我一直收到错误 TypeError: no implicit conversion of Proc into String。有人可以向我解释如何让它工作吗?

注意:我从 Rails assert_difference 方法中得到了 eval(expression, block.binding) 想法。

When it hits this line: expression = lambda{ eval(expression, block.binding) } I keep getting the error TypeError: no implicit conversion of Proc into String.

我有理由相信你 不会 在点击那条线时遇到那个错误,而是在点击这条线时:

old = expression.call

因此,异常不是由赋值触发的,而是稍后,当 Proccalled 并且 eval 被执行时。

此时会调用存储在expression中的Procexpression 中存储的 Proc 如下所示:

eval(expression, block.binding)

Kernel#eval must be something that is implicitly convertible to String (i.e. something that responds to to_str)的第一个参数,但它是Proc。因此,你得到一个 TypeError.

完成这项工作的最简单方法可能是重命名变量:

new_expression = expression
unless expression.respond_to?(:call)
  new_expression = lambda{ eval(expression, block.binding) }
end
old = new_expression.call
block.call
refute_equal old, new_expression.call

就个人而言,我可能会这样写:

module Minitest::Assertions
  def assert_changed(expression, &block)
    new_expression = if expression.respond_to?(:call)
      expression
    else
      -> { block.binding.eval(expression) }
    end

    old = new_expression.()
    block.()

    refute_equal old, new_expression.()
  end
end

没有(无用的)Module#module_eval,并使用 call 运算符语法和 -> stabby lambda 文字语法。