Rails: array.join with html in separator is escaped - ruby-on-rails

I have an array of Strings containing unsave content (user input).
I want to join these Strings in my template, separated by <br />.
I tried:
somearray.join("<br />")
But this will also escape the sparator.
Is there a workaround, keeping in mind that the content of the array absolutely must be escaped?

raw and h provide ways to apply this default behavior selectively.
<%= raw user_values_array.map {|user_value| h user_value }.join('<br />') %>
Better still, Rails 3.1 introduced safe_join(array, sep) for this purpose. Used with html_safe it can do what you need.
<%= safe_join(user_values_array, "<br />".html_safe) %>
Documentation

Is there a reason it has to be a <br /> tag? Could you use a list instead?
<ul>
<% somearray.each do |item| %>
<%= content_tag :li, item %>
<% end %>
</ul>

Have you tried this?
raw somearray.join("<br />")

Nowadays, the state-of-the-art way to solve this is:
# Inside a view:
safe_join(somearray, '<br />')
# From somewhere else, given the current controller:
controller.helpers.safe_join(somearray, '<br />')

Related

Ruby on rails raw tag inside a link_to

I have been having some problems with variables inside link_to tags, which only get to work when wrapped in a raw.
What does raw actually mean? Is it a good practice to use it to wrap strings and variables inside a tag?
From the official Rails raw documentation:
This method outputs without escaping a string. Since escaping tags is now default, this can be used when you don't want Rails to automatically escape tags. This is not recommended if the data is coming from the user's input.
It's not a good practice to use raw because it bypasses the default Rails input sanitization. Use it only if you know what you are doing.
If you need to use raw HTML inside the link to, you can also pass it as a block.
<%= link_to root_url do %>
<span>My link</span>
<% end %>
Another alternative is to use the Rails helpers which sanitizes the input.
<%= link_to content_tag(:span, "Unsafe input"), root_url %>
raw outputs without escaping the string
raw docs
Why you had problems, link_to helper accepts block as argument, you can insert any content inside link_to helper
For example:
<%= link_to 'link' do %>
<p> First paragraph </p>
<%= 'ruby string' %>
<% end %>
Will produce
<a href='/link'>
<p> First paragraph </p>
ruby string
</a>

Rails tag(:br) not considered as html_safe

Answer at the end.
I currently refactoring my app, and I'm wondering how to short my "if" since I use them a lot in it :
What I want, is a line break <br /> only if the value isn't blank, so, for now I'm writing it this way :
<% if #card.address.street.present? %>
<%= #card.address.street.titleize %>
<%= tag(:br) %> ## or plain html <br />
<% end %>
It works well, but I'm sure there is a less wordy way.
I tried :
<%= #card.address.street.titleize + tag(:br) if #card.address.street.present? %>
==> Washington Road '' ## br tag ain't html_safed.
if I do :
<%= raw #card.address.street.titleize + tag(:br) if #card.address.street.present? %>
Which is equal to :
<%== #card.address.street.titleize + tag(:br) if #card.address.street.present? %>
I works well, but expose my code to XSS attack.
So, I tried :
<%= #card.address.street.titleize + raw(tag(:br)) if #card.address.street.present? %>
<%= #card.address.street.titleize + tag(:br).html_safe if #card.address.street.present? %>
<%= #card.address.street.titleize + "<br />".html_safe if #card.address.street.present? %>
==> Washington Road '' ## br tag still ain't html_safed.
Sanitize give good result on this, used like that :
<%= sanitize(#card.address.street.titleize + tag(:br)) if #card.address.street.present? %>
But is this the best way to go regarding performance?
I ended up using an helper:
Inside helpers/application_helper
def line_break value
sanitize(value) + tag(:br)
end
Helper is loaded inside controller
class UsersController < ApplicationController
helper ApplicationHelper
I added this helper inside application one. If you want something less general on your app, a module would be prefered.
Sanitize used with empty parameters will check for html inside your string, and delete it.
If you want something less heavy, you can use h() that will escape all html instead of removing it.
In the view
<%= line_break(#card.address.street.titleize) if #card.address.street.present? %>
I know it's purely a "writing speed issue" but any help would be appreciated.
Kind Regards
This is not because your br tag is safe or unsafe.
The street string is unsafe and when you add a safe string to an unsafe string then the result is still unsafe.
You could instead write this as
<%= h(#card.address.street.titleize) + tag(:br) %>
The h does the escaping and marks the result as safe (since it has just been escaped).
Try it out:
<% street = #card.address.street %>
<%= h(street.titleize) + tag(:br) if street.present? %>
OR (less readable but compact)
<%= (h(street.titleize) + tag(:br)) if ((street = #card.address.street) && street.present?) %>
Good Luck!
Edit: As Frederick Cheung's comment brings my attention to xss attack associated with html_safe, I have updated the answer and using h for escape, Frederick. I could delete this answer, but its also showing code reduction, so may be it can help someone.

Multiple content_for for specifying classes

I have the following little problem.
In layout I yield to content_for to set up some classes on my body tag:
<body class="<%= yield(:body_classes) %>
They I would like to call content_for
<%= content_for(:body_classes, "one") %>
So far so good. I use content_for for the second time:
<%= content_for(:body_classes, "two") %>
In my HTML I get the following:
<body class="onetwo">
Is there an elegant way to separate those two classes by space? I can think of couple of hacky solution, but nothing feels right...
Many Thanks!
I don't think content_for is a good fit in this case. However, you can solve the problem elegantly with a couple of helper methods (extracted from one of my Rails projects):
def klass(*classes)
#classes = [] if #classes.nil?
#classes += classes
#classes.uniq!
nil
end
def has_klass?(klass)
!#classes.nil? && #classes.include?(klass)
end
def body_klasses
#classes.map(&:to_s).join(" ") rescue nil
end
Usage in templates:
<%= klass :one, :two %>
<%= klass :three %>
In the layout, determine if a certain class is set:
<% if has_klass? :one %>
And finally...
<body class="<%= body_klasses %>">
You can further customize these to better suit your needs.
Just put a space before (or after) the class each time you set content_for.
<% content_for(:body_classes, "one ") %>
By the way, you probably don't want the = in your setting tags; you generally don't want Rails to output into the HTML the content you're storing away for later.
Maybe you can check, before adding something to :body_clases, if thereĀ“s something already in it, in order to add the space before adding the new content.
<% content_for(:body_classes,content_for?(:body_classes) ? ' one' : 'one') %>
<% content_for(:body_classes,content_for?(:body_classes) ? ' two' : 'two') %>
The final Html will be:
<body class="one two">

Ruby on Rails seems to be auto-escaping html created by link_to

Here is my code, I'm trying to display a list of links to a bboy's crews in sentence form with .to_sentence
<span class="affiliation">
<% if(#bboy.crews.count > 0)%>
<span><%= if(#bboy.crews.size > 1) then "Crew".pluralize else "Crew" end %>:</span>
<%= #bboy.crews.collect{|c| link_to c.name, c}.to_sentence %>
<% else %>
<em>Independent</em>
<% end %>
</span>
The output I get is the correct links but it displays as:
Hustle Kidz and Knuckleheads Cali
rather than:
Hustle Kidz and
Knuckleheads
Cali
with the html escaped, rather than the desired links.
Am I missing something? I've tried CGI.unescapeHTML and a few others but am getting lost...
Rails 3 now automatically escapes everything, in order to output raw HTML use this:
<%= some_string.html_safe %>
or this:
<%= raw #some_html_string %>
Thanks to macek for a hint.
For additional details: http://markconnell.co.uk/posts/2010/02/rails-3-html-escaping
You can (and should) you the raw method
<%= raw #some_html_string %>
I agree with Kleber S, you should move this into a helper, because that's a lot of logic for a view
def crews_description(crews)
if crews.empty?
content_tag('em', 'Independent')
else
label = "Crew"
label = label.pluralize if crews.size > 1
crews_links = crews.map {|crew| link_to(h(crew.name), crew)}.to_sentence
content_tag('span', label) + crews_links.html_safe
end
end
and in your view:
<span class="affiliation">
<%= crews_description(#bboy.crews)
</span>
I recommend you move this block of code to an helper and then use the .html_safe method to obtain the expected results.

In RoR, is there an easy way to prevent the view from outputting <p> tags?

I'm new to Ruby and Rails and I have a simple controller that shows an item from the database in a default view. When it is displaying in HTML it is outputting <p> tags along with the text content. Is there a way to prevent this from happening? I suppose if there isn't, is there at least a way to set the default css class for the same output in a statement such as this:
<% #Items.each do |i| %>
<%= i.itemname %>
<div class="menu_body">
Link-1
</div>
<% end %>
So the problem is with the <%= i.itemname %> part. Is there a way to stop it from wrapping it in its own <p> tags? Or set the css class for the output?
Thanks!
You need to enclose it with the HTML tag of your choice. Also if required you can escape bad code by using <%=h i.itemname %> Example:
<% #Items.each do |i| %>
<div><%=h i.itemname %></div>
<div class="menu_body">
Link-1
</div>
<% end %>
Edit: Ryan Bigg is right. Rails doesn't output a <p> tag. Sorry for the wrong info.
You canchange the public/stylesheets/scaffold.css if you want.
Or if you want to change it for a single page say items/index.html.erb
<style>
p{
/* your style here *?
}
</style>

Resources