Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change the context/binding inside a block in ruby

I have a DSL in Ruby that works like so:

desc 'list all todos'
command :list do |c|
  c.desc 'show todos in long form'
  c.switch :l
  c.action do |global,option,args|
    # some code that's not relevant to this question
  end
end

desc 'make a new todo'
command :new do |c|
  # etc.
end

A fellow developer suggested I enhance my DSL to not require passing c to the command block, and thus not require the c. for all the methods inside; presumably, he implied I could make the following code work the same:

desc 'list all todos'
command :list do
  desc 'show todos in long form'
  switch :l
  action do |global,option,args|
    # some code that's not relevant to this question
  end
end

desc 'make a new todo'
command :new do
  # etc.
end

The code for command looks something like

def command(*names)
  command = make_command_object(..)
  yield command                                                                                                                      
end

I tried several things and was unable to get it to work; I couldn't figure out how to change the context/binding of the code inside the command block to be different than the default.

Any ideas on if this is possible and how I might do it?

like image 558
davetron5000 Avatar asked May 01 '11 20:05

davetron5000


2 Answers

Paste this code:

  def evaluate(&block)
    @self_before_instance_eval = eval "self", block.binding
    instance_eval &block
  end

  def method_missing(method, *args, &block)
    @self_before_instance_eval.send method, *args, &block
  end

For more information, refer to this really good article here

like image 95
Jatin Ganhotra Avatar answered Oct 21 '22 06:10

Jatin Ganhotra


Maybe

def command(*names, &blk)
  command = make_command_object(..)
  command.instance_eval(&blk)
end

can evaluate the block in the context of command object.

like image 29
Sony Santos Avatar answered Oct 21 '22 06:10

Sony Santos