html_safe and helpers in rails 3. Mystery solved.

html_safe and helpers in rails 3 – Rails 2 vs 3

In Rails 3 (contrary to Rails 2) Strings are automatically escaped using ERB::Util.h(string) and the reason is simple. In 95% of the cases you won’t have to print html code inside ruby strings like this:
<%="<h1>Hello World</h1>"%>
In Rails 2 for this 95% of the cases you had to call the “h” helper to escape the output which was really annoying and insecure (if you forgot one).

Although this was the case for views, constructing a helper that prints html content became tricky in Rails 3. If you want your html content to stay unescaped it has to be an ActiveSupport::SafeBuffer instance instead of a String instance. To achieve this there are two solutions:

html_safe and helpers in rails 3 – constructing a helper

The first one is using html_safe method:

# helper.rb
def foo
    html = ""
    html += "<strong>bar</strong>"
    html.html_safe
end

<%=foo%>

This will output html correctly. A similar approach would be to use safe_concat inside your helper like this:

# helper.rb
def foo
    safe_concat("<strong>bar</strong>")
    return
end

<%=foo%>

In every case you can use raw() view helper to keep your string unescaped:

# helper.rb
def foo
    "<strong>bar</strong>"
end

<%=raw(foo)%>

html_safe and helpers in rails 3 – the tricky part

I recently came up with this case:

#helper.rb
def foo
    html = content_tag :p, "bar"
    html += "<strong>another bar</strong>"
    html.html_safe
end

It’s output was something that I wasn’t expecting. The <strong> tags were escaped while the <p> tags remained unescaped. This made me suspicious of the concat method that ActiveSupport::SafeBuffer implements, which is the following:

def concat(value)
    if value.html_safe?
        super(value)
    else
        super(ERB::Util.h(value))
     end
end

As you can see if the argument of concat is a String (html_safe? == false) the value will be escaped and then concated.
So if you want to concat String and ActiveSupport::SafeBuffer instances always begin from a String instance even if it’s an empty string:

#helper.rb
def foo
    html = ""
    html += content_tag :p, "bar"
    html += "<strong>another bar</strong>"
    html.html_safe
end

This will do the job for you!

2 Comments html_safe and helpers in rails 3. Mystery solved.

Leave a Reply to Paweł Gościcki Cancel reply

Your email address will not be published. Required fields are marked *


*