I'm Mo I'm Mo - 16 days ago 6
Ruby Question

Ruby DSL nested constructs

I am using the following code to enforce context of DSL nested constructs. What are the other ways of achieving the same functionality?

def a &block
p "a"
def b &block
p "b"
def c &block
p "c"
instance_eval &block
end
instance_eval &block
undef :c
end
instance_eval &block
undef :b
end
# Works
a do
b do
c do
end
end
end

# Doesn't Work
b do
end
c do
end


Source

Answer

You asked about other ways, not the best way. So here's my solution :

class A
  def initialize
    p "a"
  end

  def b &block
    B.new.instance_eval &block
  end
end

class B
  def initialize
    p "b"
  end

  def c &block
    C.new.instance_eval &block
  end
end

class C
  def initialize
    p "c"
  end
end

def a &block
  A.new.instance_eval &block
end

A bit shorter :

class A
  def b &block
    p "b"
    B.new.instance_eval &block
  end

  class B
    def c &block
      p "c"
      C.new.instance_eval &block
    end

    class C
    end
  end
end

def a &block
  p "a"
  A.new.instance_eval &block
end

If you don't plan to have a d method for an A::B::C object :

def a &block
  p "a"
  A.new.instance_eval &block
end

class A
  def b &block
    p "b"
    B.new.instance_eval &block
  end

  class B
    def c &block
      p "c"
      instance_eval &block
    end
  end
end

This was a fun one :

def new_class_and_method(klass_name, next_klass=Object)
  dynamic_klass = Class.new do
    define_method(next_klass.name.downcase){|&block| p next_klass.name.downcase; next_klass.new.instance_eval &block}
  end
  Object.const_set(klass_name, dynamic_klass)
end

new_class_and_method("A", new_class_and_method("B", new_class_and_method("C")))

def a &block
  p "a"
  A.new.instance_eval &block
end
Comments