在 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
因此,异常不是由赋值触发的,而是稍后,当 Proc
被 call
ed 并且 eval
被执行时。
此时会调用存储在expression
中的Proc
。 expression
中存储的 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 文字语法。
我正在尝试创建一个类似于 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 errorTypeError: no implicit conversion of Proc into String
.
我有理由相信你 不会 在点击那条线时遇到那个错误,而是在点击这条线时:
old = expression.call
因此,异常不是由赋值触发的,而是稍后,当 Proc
被 call
ed 并且 eval
被执行时。
此时会调用存储在expression
中的Proc
。 expression
中存储的 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 文字语法。