在Ruby的输出中需要帮助缩进标记

时间:2014-12-01 04:09:06

标签: ruby

更新:好的,所以我实现了你的代码,但现在缩进没有显示出来!什么想法可能是错的?我修改了代码,以便它会尝试通过我的原始测试(这只是一个练习,所以在现实生活中我不会覆盖XmlDocument类),这里是修改后的代码:

class XmlDocument

attr_reader :indent_depth, :bool

def initialize(bool = false, indent_depth = 0)
    @indent_depth = indent_depth
    @bool = bool
end

def method_missing(name, *args)
    indentation = '  '*indent_depth
    attrs = (args[0] || {}).map { |k, v| " #{k}='#{v}'" }.join(' ')
    if block_given?
        puts indent_depth
        opening = "#{indentation}<#{name}#{attrs}>"
        contents = yield(XmlDocument.new(true,indent_depth+1))
        closing = "#{indentation}</#{name}>"
        bool ? opening + "\n" + contents + "\n" + closing : opening + contents + closing
    else
        "#{indentation}<#{name}#{attrs}/>"
    end
end
end

我试图让方法通过此测试:

it "indents" do
@xml = XmlDocument.new(true)
@xml.hello do
  @xml.goodbye do
    @xml.come_back do
      @xml.ok_fine(:be => "that_way")
    end
  end
end.should ==
"<hello>\n" +
"  <goodbye>\n" +
"    <come_back>\n" +
"      <ok_fine be='that_way'/>\n" +
"    </come_back>\n" +
"  </goodbye>\n" +
"</hello>\n"

...但我不确定下面的代码去哪里。我正在考虑使用计数器来跟踪我们必须走多远。我尝试了一些代码,但随后将其删除,因为它太乱了,我觉得缩进不应该太复杂而无法实现。

class XmlDocument

    def initialize(bool = false)
        @bool = bool
    end

    def send(tag_name)
        "<#{tag_name}/>"
    end

    def method_missing(meth, arg={}, &block)

        arbitrary_method = meth.to_s
        tag_string = ''

        # 1) test for block
        # 2) test for arguments
        # 3) test for hash
        if block_given? # check for @xml.hello do; @xml.goodbye; end
            if yield.class == String # base case: @xml.hello do; "yellow"; end
                "<#{arbitrary_method}>#{yield}</#{arbitrary_method}>"
            else # in the block we do not have a string, we may have another method
                method_missing(yield)
            end
        elsif arg.empty? # no arguments e.g. @xml.hello
            send(arbitrary_method)
        else # hash as argument e.g. @xml.hello(:name => 'dolly')
            send("#{arbitrary_method} #{arg.keys[0]}='#{arg.values[0]}'")
        end


    end

end

1 个答案:

答案 0 :(得分:1)

您的代码需要大量工作 - 一些指示:

  • 不要覆盖send方法!
  • 不要一遍又一遍地打电话给yield - 你不知道你可能会造成什么副作用,更不用说性能打击了 - 打电话一次,并记住返回值。 / LI>
  • 您可能想要了解如何编写DSL(here是关于该主题的博客文章),以了解它是如何在其他地方正确完成的。

忽略上述内容,我会尝试回答有关缩进的问题。

在DSL用例中,您可能希望使用将缩进深度保持为状态的上下文对象:

class Indented
  attr_reader :indent_depth
  def initialize(indent_depth = 0)
    @indent_depth = indent_depth
  end

  def method_missing(name, *args)
    indentation = '  '  * indent_depth
    attrs = (args[0] || {}).map { |k, v| "#{k}='#{v}'" }.join(' ')
    if block_given?
      "#{indentation}<#{name} #{attrs}>\n" + 
        yield(Indented.new(indent_depth + 1)) + 
      "\n#{indentation}</#{name}>"
    else

      "#{indentation}<#{name} #{attrs}/>"
    end
  end
end

xml = Indented.new
puts xml.hello do |x|
  x.goodbye do |x|
    x.come_back do |x|
      x.ok_fine(:be => "that_way")
    end
  end
end    
# => <hello >
# =>   <goodbye >
# =>     <come_back >
# =>       <ok_fine be='that_way'/>
# =>     </come_back>
# =>   </goodbye>
# => </hello>