更新:好的,所以我实现了你的代码,但现在缩进没有显示出来!什么想法可能是错的?我修改了代码,以便它会尝试通过我的原始测试(这只是一个练习,所以在现实生活中我不会覆盖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
答案 0 :(得分:1)
您的代码需要大量工作 - 一些指示:
send
方法!yield
- 你不知道你可能会造成什么副作用,更不用说性能打击了 - 打电话一次,并记住返回值。 / LI>
忽略上述内容,我会尝试回答有关缩进的问题。
在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>