From: Isaac Sanders Date: 2011-11-07T21:04:45+09:00 Subject: Re: Tricky DSL, how to do it? --20cf303f67b83d35de04b123dc0b Content-Type: text/plain; charset=ISO-8859-1 I really think that we should all take another look at the Ruby Object Model. Dave Thomas has a great screencast on it. This question would be answered if the ROM was analyzed closely. On Mon, Nov 7, 2011 at 6:56 AM, Robert Klemme wrote: > 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/ > > -- Sincerely, Isaac Sanders Section C-4B Vice Chief, Order of the Arrow Vice Chief of Administration, Tecumseh #65 Eagle Scout --20cf303f67b83d35de04b123dc0b--