Reputation:
A user of my Ruby DSL wishes to be able to add additional methods and invoke them within the context of an arbitrary block, for example:
def do_something
override 'flags', 'ABC'
end
project('XXX') do
configuration('debug') do
do_something
end
end
In this sample, 'override' is a method of the configuration object that's invoked with instance_eval.
Is there a way of forcing any method calls to be executed within the context of the configuration block, so that 'override' for example can be resolved and executed?
[edit]
So I construct the root DSL object and evaluate as follows:
root_dsl = DSL.new
root_dsl.instance_eval(File.read(filename))
This DSL class has a method on it matching 'project' from the above sample:
def project(&block) do
project_dsl = ProjectDSL.new
project_dsl.instance_eval(&block)
end
And the project_dsl class has a method on it matching 'configuration' from the above example:
def configuration(name, &block) do
configuration_dsl = ProjectConfigDSL.new(name)
configuration_dsl.instance_eval(&block)
end
And so on. The user wants his defined method in the DSL file itself to execute within the context of the ProjectConfigDSL object.
Upvotes: 1
Views: 335
Reputation:
So I was unaware that instance_eval sandboxes scope to that instance, so what I've had to do is delegate from the root DSL object with executing the block directly and forwarding calls in missing_method to a child DSL object, and repeat this pattern the whole way down.
By doing this it fixes the scoping issue, but it does cause a new issue of collliding method names being forwarded to the wrong objects. Fortunately my DSL hasn't run into that issue yet, so this will be good enough for now.
Upvotes: 0
Reputation: 26758
The way I'm doing this in my own code is to make the helper function return a proc that gets passed to instance_eval. for example:
def do_something
Proc.new do
override 'flags', 'ABC'
end
end
project('XXX') do
configuration 'debug', &do_something
end
Upvotes: 0