On Sun, Nov 6, 2011 at 10:35 PM, Intransition <transfire / gmail.com> wrote:
> I'd want to write a DSL such that a surface method_missing catches undefi=
ned
> methods and records the blocks that they define e.g.
> =A0 foo do
> =A0 =A0 puts "foo"
> =A0 end
> So I would end up with:
> =A0 { :foo=3D>#<Proc ...> }
> Presently I have something like this:
>
> =A0 #
> =A0 class Evaluator < BasicObject
> =A0 =A0 def initialize(&block)
> =A0 =A0 =A0 @__config__ =3D {}
> =A0 =A0 =A0 instance_eval(&block) if block
> =A0 =A0 end
> =A0 =A0 def __config__
> =A0 =A0 =A0 @__config__
> =A0 =A0 end
> =A0 =A0 def method_missing(sym, *args, &block)
> =A0 =A0 =A0 @__config__[sym] =3D block
> =A0 =A0 end
> =A0 end
> However when I call on a block I want it to evaluate as if in the definin=
g
> context=A0(in this case toplevel), not inside the "DSL" class that evalua=
ted
> via method_missing.
> =A0 e =3D Evaluator.new do
> =A0 =A0 foo do
> =A0 =A0 =A0 puts "foo"
> =A0 =A0 end
> =A0 end
>
> =A0 e.__config__[:foo].call
> Instead of what I want, I get a method missing error for #puts.
> Any ideas?

You could capture the calling self by getting it from the binding of
the block and hide the evaluating call behind your own #eval method:

class Evaluator < BasicObject
  def initialize(&block)
    @__config__ =3D {}
    if block
      @context =3D block.binding.eval("self")
      instance_eval(&block)
    end
  end

  def __config__
    @__config__
  end

  def method_missing(sym, *args, &block)
    @__config__[sym] =3D block
  end

  def eval(sym, *args)
    @context.instance_exec(*args, &@__config__[sym])
  end

end

e =3D Evaluator.new do
  foo do
    puts "foo"
  end

  bar do |txt|
    puts "txt =3D #{txt}"
  end
end

e.eval(:foo)               # =3D> "foo"
e.eval(:bar, "hello")    # =3D> "txt =3D hello"


Regards,
Sean