Reputation: 41
I'm trying to format tags using "method_missing", the result I want is shown below.
<foo>\n
<bar>\n
<ab/>\n
</bar>\n
</foo>\n
I believe a running index is required but I'm unclear where best to put it or whether that's the best approach. How would I add indentation?
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"){}}}
I get this wrong output (not certain whats causing the extra \n)
<foo>\n
--<bar>\n
----<ab>\n
----</bar>\n
\n
--</foo>\n
Upvotes: 3
Views: 155
Reputation: 1965
You can use call stack to get the level of indentation. See caller
in my code.
I also suggest to isolate your code in some class, so that you can use more tags, because there could be a lot of methods defined in main
So, I modified your code a bit:
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
Example:
> 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>
Upvotes: 1