I'm trying to do a DSL, in which the user can pass a block and expect an instance variable @arg to be defined. This is a full example with a unit test failing:
# 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
Using pry or putting puts statements inside the block, I can see that @arg is nil. But Foo.new.instance_variable_get :@arg correctly outputs foo, so it must be related to some scoping rules.
What do I need to change in the implementation to make the test pass and the DSL to work?
@argof the class in which I'm callingfilter. How could I tweak the implementation then so the@argin the block gets evaluated in the scope of the instantiated class?filteris an instance method.filters, which sets upfilter, is a class method. However,filterscaptures a block in a class context and passes it on tofilter; OP wishes to have the block executed in the instance context. (Not saying it's possible or impossible just yet, my brain hurts.)filtersso I can create the DSL in the post (which is part of a bigger project), or at least I think I do. The end objective here is to tweak the implementation so the test passes for the example filterFoothat I have created.filterandfilterswere the same. I missed thedefine_methodcall.