所以,如果我循环并创建一个li / a标签的集合,我会按预期得到..这些标签的数组:
(1..5).to_a.map do
content_tag(:li) do
link_to("boo", "www.boohoo.com")
end
end
=> ["<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>"]
我打电话给他们加入我得到一个预期的字符串...
(1..5).to_a.map do
content_tag(:li) do
link_to("boo", "www.boohoo.com")
end
end.join
=> "<li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li>"
但是,如果我将这一级别更深地嵌入ol标签......
content_tag(:ol) do
(1..5).to_a.map do
content_tag(:li) { link_to("boo", "www.boohoo.com") }
end.join
end
=> "<ol><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li></ol>"
我逃脱了内心的疯狂!!!
查看rails源代码:
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
content_tag_string(name, capture(&block), options, escape)
else
content_tag_string(name, content_or_options_with_block, options, escape)
end
end
private
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
"<#{name}#{tag_options}>#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
end
看起来我只能这么做:content_tag(:li,nil,nil,false)并没有让它逃脱内容..但是:
content_tag(:ol, nil, nil, false) do
(1..5).to_a.map do
content_tag(:li, nil, nil, false) do
link_to("boo", "www.boohoo.com")
end
end.join
end
=> "<ol><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li><li><a href="www.boohoo.com">boo</a></li></ol>"
我仍然患有不必要的html_escape综合症......
所以我知道避免这种情况的唯一方法是:
content_tag(:ol) do
(1..5).to_a.map do
content_tag(:li) do
link_to("boo", "www.boohoo.com")
end
end.join.html_safe
end
=> "<ol><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li></ol>"
但是..为什么会这样?
答案 0 :(得分:15)
这是因为在Rails 3中引入了SafeBuffer类,它包装String类并覆盖在调用concat时可能发生的默认转义。
在您的情况下,content_tag(:li)正在输出正确的SafeBuffer,但是Array#join不能理解SafeBuffers并且只是输出一个String。然后使用String作为值而不是SafeBuffer调用content_tag(:ol)并将其转义。所以它与嵌套没有多大关系,因为它与join返回String而不是SafeBuffer有关。
在String上调用html_safe,将String传递给raw,或者将数组传递给safe_join都会返回正确的SafeBuffer并阻止外部content_tag转义它。
现在在将false传递给escape参数的情况下,当您将块传递给内容标记时,这不起作用,因为它正在调用capture(&block)
ActionView :: Helpers :: CaptureHelper用于引入模板,或者你的情况是join的输出值,然后它会在字符串进入html_escape
方法之前调用字符串上的content_tag_string
。
# action_view/helpers/tag_helper.rb
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
# capture(&block) escapes the string from join before being passed
content_tag_string(name, capture(&block), options, escape)
else
content_tag_string(name, content_or_options_with_block, options, escape)
end
end
# action_view/helpers/capture_helper.rb
def capture(*args)
value = nil
buffer = with_output_buffer { value = yield(*args) }
if string = buffer.presence || value and string.is_a?(String)
ERB::Util.html_escape string
end
end
由于此处的值是join的返回值,而join返回一个String,因此在content_tag代码进入它之前,它会调用html_escape并自行转义。
感兴趣的人的一些参考链接
https://github.com/rails/rails/blob/v3.1.0/actionpack/lib/action_view/helpers/capture_helper.rb
https://github.com/rails/rails/blob/v3.1.0/actionpack/lib/action_view/helpers/tag_helper.rb
http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/
http://railsdispatch.com/posts/security
修改强>
处理此问题的另一种方法是执行map / reduce而不是map / join,因为如果reduce没有传递参数,它将使用第一个元素并使用该对象运行给定的操作,对于map content_tag将在SafeBuffer上调用该操作。
content_tag(:ol) do
(1..5).to_a.map do
content_tag(:li) do
link_to(...)
end
end.reduce(:<<)
# Will concat using the SafeBuffer instead of String with join
end
作为单行
content_tag(:ul) { collection.map {|item| content_tag(:li) { link_to(...) }}.reduce(:<<) }
添加一些元香料来清理东西
ul_tag { collection.map_reduce(:<<) {|item| li_link_to(...) } }
谁需要html_safe ......这是Ruby!
答案 1 :(得分:5)
如果您使用safe_join
会怎样?
content_tag(:ol) do
safe_join (1..5).to_a.map {
content_tag(:li) { link_to("boo", "www.boohoo.com") }
}, ''
end
或者只是使用raw?
content_tag(ol) do
1.upto(5) {
raw content_tag(:li) { link_to 'boo', 'www.boohoo.com' }
# or maybe
# raw content_tag(:li) { raw link_to('boo', 'www.boohoo.com') }
}
end
答案 2 :(得分:0)
不是正面的,但我认为html转义发生在每个“层”(缺少一个更好的术语;每次迭代) - 我的意思是在内部块级别(1..5)....然后在外部块级别(content_tag(:ol)do ...