I am building a block of HTML code using Rails' content_tag
helper. The challenge I face right now is joining HTML strings from an array with HTML elements generated by content_tag
.
RuboCop Rails/OutputSafety reference.
For example:
options = ["<li>Three</li>", "<li>Four</li>", "<li>Five</li>"]
# This is code to generate blocks of HTML
out = []
out << content_tag(:ul,
content_tag(:li, "One") +
content_tag(:li, "Two") +
options.join(''),
:class => ["class_1", "class_2"])
safe_join(out)
# Expect result should be like
<ul class="class_1 class_2">
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
# Actual result
<ul class="class_1 class_2">
<li>One</li>
<li>Two</li>
"<li>Three</li><li>Four</li><li>Five</li>"
</ul>
However, if I use the html_safe approach like below, it will work.
%{<ul>
<li>One</li>
<li>Two</li>
#{options.join('')}
</ul>
}.html_safe
Any suggestions on what I should change?
# New apporach
options = ["Three", "Four", "Five"]
out = []
out << content_tag(:ul,
content_tag(:li, "One") +
content_tag(:li, "Two") +
options.collect do |option|
content_tag(:li, "#{option[0]}")
end.join(""),
:class => ["class_1", "class_2"])
safe_join(out)
# New approach result
<ul class="class_1 class_2">
<li>One</li>
<li>Two</li>
"<li>Three</li><li>Four</li><li>Five</li>"
</ul>
The problem is that you are concatenating your output with unsafe strings coming from
options
array. This is the only place where you should usehtml_safe
method for the whole output to be safe:Edit
First of all
safe_join
method does not work likehtml_safe
method, it doesn't only make the joined strings html_safe. It also makes html escaping if the joined string are not html_safe in order to avoid harmful content.https://apidock.com/rails/ActionView/Helpers/OutputSafetyHelper/safe_join
In your case
safe_join
method didn't do anything at all with the strings inout
array, since they had already been html_safe.The cause of the problem is that you concatenated a safe string with unsafe one:
At that moment
options.join('')
was html escaped since it wasn't safe. See the example:So, in order to get expected results 2 conditions must be met:
safe_join
method must take array of html_safe strings. If they are not html_safe, all html tags will be escaped.As you can see, you did not fulfill the second condition.
Suggestions about the new approach
.join("")
method makes the result string unsafe, even if the array contains safe strings. Usesafe_join
: