user1282993
user1282993

Reputation:

Ruby DSL - invoking user-provided methods within block context

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

Answers (2)

user1282993
user1282993

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

max pleaner
max pleaner

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

Related Questions