使用 define_method 时块中的实例变量
instance variable in block when using define_method
我正在尝试做一个 DSL,用户可以在其中传递一个块并期望定义一个实例变量 @arg
。这是单元测试失败的完整示例:
# Implementation
class Filter
def initialize
@arg = 'foo'
end
def self.filters &block
define_method :filter do |els|
els.select &block
end
end
end
# Usage
class Foo < Filter
filters {|el| el == @arg}
end
# Expected behavior
describe 'filters created with the DSL' do
subject { Foo.new }
it 'can use @arg in the filters block' do
els = %w[notthearg either foo other]
expect(subject.filter els).to be_eql(['foo'])
end
end
使用 pry
或将 puts
语句放入块中,我可以看到 @arg
为零。但是Foo.new.instance_variable_get :@arg
正确的输出了foo
,所以一定是和一些作用域规则有关。
我需要在实现中更改什么才能使测试通过并使 DSL 正常工作?
instance_exec
救援!
class Filter
def initialize
@arg = 'foo'
end
def self.filters &block
define_method :filter do |els|
els.select { |e| self.instance_exec(e, &block) }
end
end
end
class Foo < Filter
filters {|el| el == @arg }
end
Foo.new.filter(%w[notthearg either foo other])
# => ["foo"]
警告:确保这 非常 有据可查,因为任何涉及 instance_exec
或其表兄弟的恶作剧都在左右打破程序员的期望 - 通过设计,你'重新破坏"scope"的概念。我很确定 OP 知道这一点,但它值得写在众所周知的纸上。
此外,请考虑使用访问器而不是普通实例变量 - 检查访问器,而不检查变量。即 { |el| el == urg }
将导致错误,但 { |el| el == @urg }
将静默失败(并过滤 nil
)。
我正在尝试做一个 DSL,用户可以在其中传递一个块并期望定义一个实例变量 @arg
。这是单元测试失败的完整示例:
# Implementation
class Filter
def initialize
@arg = 'foo'
end
def self.filters &block
define_method :filter do |els|
els.select &block
end
end
end
# Usage
class Foo < Filter
filters {|el| el == @arg}
end
# Expected behavior
describe 'filters created with the DSL' do
subject { Foo.new }
it 'can use @arg in the filters block' do
els = %w[notthearg either foo other]
expect(subject.filter els).to be_eql(['foo'])
end
end
使用 pry
或将 puts
语句放入块中,我可以看到 @arg
为零。但是Foo.new.instance_variable_get :@arg
正确的输出了foo
,所以一定是和一些作用域规则有关。
我需要在实现中更改什么才能使测试通过并使 DSL 正常工作?
instance_exec
救援!
class Filter
def initialize
@arg = 'foo'
end
def self.filters &block
define_method :filter do |els|
els.select { |e| self.instance_exec(e, &block) }
end
end
end
class Foo < Filter
filters {|el| el == @arg }
end
Foo.new.filter(%w[notthearg either foo other])
# => ["foo"]
警告:确保这 非常 有据可查,因为任何涉及 instance_exec
或其表兄弟的恶作剧都在左右打破程序员的期望 - 通过设计,你'重新破坏"scope"的概念。我很确定 OP 知道这一点,但它值得写在众所周知的纸上。
此外,请考虑使用访问器而不是普通实例变量 - 检查访问器,而不检查变量。即 { |el| el == urg }
将导致错误,但 { |el| el == @urg }
将静默失败(并过滤 nil
)。