From: Robert Klemme Date: 2011-11-07T20:56:54+09:00 Subject: Re: Tricky DSL, how to do it? On Sun, Nov 6, 2011 at 11:35 PM, Intransition wrote: > I'd want to write a DSL such that a surface method_missing catches undefined > methods and records the blocks that they define e.g. >   foo do >     puts "foo" >   end > So I would end up with: >   { :foo=># } > Presently I have something like this: > >   # >   class Evaluator < BasicObject >     def initialize(&block) >       @__config__ = {} >       instance_eval(&block) if block >     end >     def __config__ >       @__config__ >     end >     def method_missing(sym, *args, &block) >       @__config__[sym] = block >     end >   end > However when I call on a block I want it to evaluate as if in the defining > context (in this case toplevel), not inside the "DSL" class that evaluated > via method_missing. >   e = Evaluator.new do >     foo do >       puts "foo" >     end >   end > >   e.__config__[:foo].call > Instead of what I want, I get a method missing error for #puts. > Any ideas? You can't have both (evaluate with self = Evaluator to trigger Evaluator's method missing and invoking the block in the defining context) at the same time. Question is, what happens if a missing method is invoked? In your implementation the block is returned. I would at least change that to return nil to avoid nasty side effects. But generally execution cannot reasonably continue. What about catching the exception and recording it? 12:54:59 ~$ ./x.rb {:foo=>#} 12:55:02 ~$ cat -n x.rb 1 #!/bin/env ruby19 2 3 class Evaluator < BasicObject 4 def initialize(&block) 5 @__config__ = {} 6 7 if block 8 begin 9 block.call 10 rescue ::NoMethodError => e 11 @__config__[e.name] = block 12 nil 13 end 14 end 15 end 16 17 def __config__ 18 @__config__ 19 end 20 21 end 22 23 24 e = Evaluator.new do 25 foo do 26 puts "foo" 27 end 28 end 29 30 p e.__config__ 31 12:55:03 ~$ Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/