2

我正在学习元编程,并正在尝试制作一些 DSL 来生成 HTML。@result实例变量没有生成正确的答案,因为在调用h1方法时,@result实例变量被重置。有没有一种优雅的方式来处理这些“嵌套”方法调用(我知道Ruby 在技术上没有嵌套方法)。这是我的代码:

class HtmlDsl
  attr_reader :result
  def initialize(&block)
    instance_eval(&block)
  end

  private

  def method_missing(name, *args, &block)
    tag = name.to_s
    content = args.first
    @result = "<#{tag}>#{block_given? ? instance_eval(&block) : content}</#{tag}>"
  end
end

html = HtmlDsl.new do
  html do
    head do
      title 'yoyo'
    end
    body do
      h1 'hey'
    end
  end
end
p html.result # => "<html><body><h1>hey</h1></body></html>"
# desired result # => "<html><head><title>yoyo</title></head><body><h1>hey</h1></body></html>"
4

1 回答 1

4

您的问题不在于@result被重置,只是您添加到@result返回值中instance_eval(&block),这是块中的最后一行,而不是聚合块。这应该会更好(尽管不完美):

class HtmlDsl
  attr_reader :result
  def initialize(&block)
    instance_eval(&block)
  end

  private

  def method_missing(name, *args, &block)
    tag = name.to_s
    content = args.first
    (@result ||= '') << "<#{tag}>"
    if block_given?
      instance_eval(&block)
    else
      @result << content
    end
    @result <<  "</#{tag}>"
  end
end

所以现在:

html = HtmlDsl.new do
  html do
    head do
      title 'yoyo'
    end
    body do
      h1 'hey'
    end
  end
end
p html.result
#=> "<html><head><title>yoyo</title></head><body><h1>hey</h1></body></html>" 

我所做的是,每个调用实际上都将一个片段呈现给@result,因此内部调用呈现内部片段,每个调用都用标签包装自己的内部片段。

于 2014-05-05T10:45:41.843 回答