Ruby中标签的递归缩进

时间:2013-06-12 08:57:59

标签: ruby

我正在尝试使用“method_missing”格式化标签,我想要的结果如下所示。

<foo>\n
    <bar>\n
        <ab/>\n
    </bar>\n
</foo>\n

我认为需要运行索引,但我不清楚最佳位置,或者这是否是最好的方法。我该如何添加缩进?

def method_missing(meth, *args, &block)
    if args.length > 0
        my_other_method(args)
    else
        my_method(meth.to_s, &block)
    end
end

def my_other_method(args)
    "<#{args}/>"
end

def my_method(meth)     
    s = "<#{meth}>\n"
    s << "#{_indentation}"
    s << yield.to_s << "\n"
    s << "#{_indentation}"
    s << "</#{meth}>\n"
end

def _indentation
    ("--" * _level.to_i)    # dashes added to more easily infer spacing
end

def _level
    caller.rindex {|val| val.scan('my_method')} / 3
end

p foo{bar{baz(:a => "b"){}}}

我得到了错误的输出(不确定是什么导致额外的\ n)

<foo>\n
--<bar>\n
----<ab>\n
----</bar>\n
\n
--</foo>\n

1 个答案:

答案 0 :(得分:1)

您可以使用调用堆栈来获取缩进级别。请参阅我的代码中的caller。 我还建议在某些类中隔离您的代码,以便您可以使用更多标记,因为main中可能会定义很多方法 所以,我稍微修改了你的代码:

class TagFormatter
  def method_missing(method_name, *args, &block)
    _tagify method_name, args, &block
  end

  def _tagify(tag_name, args)
    if block_given?
      s = "#{_indentation}<#{tag_name}#{_attrs args}>\n"
      s << (yield || "")
      s << "#{_indentation}</#{tag_name}>\n"
    else
      "#{_indentation}<#{tag_name}#{_attrs args}/>\n"
    end
  end

  def _indentation
    "    " * _level
  end

  def _level
    caller.index { |val| val =~ /instance_eval/ } / 3 - 1
  end

  def _attrs(args)
    args.map { |arg| _to_attr arg }.join if args
  end

  def _to_attr(arg)
    if arg.kind_of? Hash
      arg.map { |k, v| %Q{ #{k}="#{v}"}}
    else
      %Q{ #{arg.to_s}}
    end
  end
end

示例:

> tf = TagFormatter.new
> puts tf.instance_eval 'foo{bar{baz(:a => "b"){}}}'
<foo>
    <bar>
        <baz a="b">
        </baz>
    </bar>
</foo>
> puts tf.instance_eval 'foo{bar{baz(:a => "b")}}' # no block given in baz
<foo>
    <bar>
        <baz a="b"/>
    </bar>
</foo>
> puts tf.instance_eval 'foo{bar{baz("disabled", :readonly, :a=>"b")}}'
<foo>
    <bar>
        <baz disabled readonly a="b"/>
    </bar>
</foo>