【问题标题】:Access variable from the block's scope inside a method call in Ruby在 Ruby 中的方法调用内从块的范围访问变量
【发布时间】:2016-08-29 01:23:46
【问题描述】:

我正在尝试创建一个类似于 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) 的想法。

【问题讨论】:

  • 虽然我没有看到你在你的问题中这样做,但这看起来确实像你在尝试评估捕获的块时遇到的错误。
  • 我猜这是因为你重新定义了变量 expression 然后你在 lambda 中使用

标签: ruby-on-rails ruby lambda metaprogramming proc


【解决方案1】:

当它到达这一行时:expression = lambda{ eval(expression, block.binding) } 我不断收到错误TypeError: no implicit conversion of Proc into String

我有理由确定您确实不会在点击该行时遇到该错误,而是在点击该行时遇到该错误:

old = expression.call

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

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

eval(expression, block.binding)

Kernel#eval 的第一个参数必须是可以隐式转换为String 的东西(即响应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 文字语法。

【讨论】:

  • 谢谢你很好地解释了这个问题。当错误指向不同的行时,它让我陷入了循环。
猜你喜欢
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
相关资源
最近更新 更多