Returning HTML from a Ruby Gem - ruby-on-rails

I'm trying to create a Ruby gem that returns html mark up like so:
class Hola
def self.hi(name = "world")
"hello #{name}"
end
def self.hi_with_markup(name = "world")
"<strong>hello #{name}</strong>"
end
end
However, whenever I try to use it in a test.html.erb file like so:
<%= Hola.hi_with_markup(", please work!") %>
It returns the string with the tags printed instead of actually rendering the html. How can I fix this from the gem side?
Thanks!

In Rails 3 the default changed from "not" escaping HTML to escaping HTML (i.e. converting things like '>' to >) for any String deemed to be unsafe; which is generally any string that has the potential to have user characters, including the output of your gem. There are two ways around this raw() and .html_safe.
Here's a comprehensive answer: raw vs. html_safe vs. h to unescape html
The short answer is to do this:
<%= Hola.hi_with_markup(", please work!").html_safe %>
or
<%= raw(Hola.hi_with_markup(", please work!")) %>

Try this:
class Hola
def self.hi(name = "world")
"hello #{name}"
end
def self.hi_with_markup(name = "world")
"<strong>hello #{name}</strong>".to_html
end
end

Related

add <a> tag to all items in hash - Ruby on Rails

This is continuation from This one. Read following question&comments first before looking at this one.
I have following Replacement(Hash) below for switching text on html that I've got from desired url of website using open-uri library.
replacements = {'1杯の'=>'a cup of','たくさん'=>'a lot','たくさんの'=>'a lot of','1週間'=>'a week','午前'=>'a.m.','およそ'=>'about','住所'=>'adress','大人'=>'adult','アフリカ'=>'Africa',‌​'の後で'=>'after','のあとで'=>'after','午後'=>'afternoon','再び'=>'again','ふたたび'=>'again','空‌​港'=>'airport','全て'=>'all','一日中'=>'all day','1日中'=>'all day','いつも'=>'always'...}
And I want to add <a> tag to all english word at hash above, which is linked to online Japanese-English Dictionary service called "Weblio"
Weblio's url format is like this: http://ejje.weblio.jp/{desired english word}
so that <a>tag which should be added to all english word should be like below.
DESIRED+ENGLISH+WORD
Any way to do the easily? I just started learning Ruby on Rails recently, so it would be nice to have detailed and simple explanation for it with some examples.
EDIT
def submit
require 'uri'
require 'open-uri'
require 'nokogiri'
charset = nil
#html = open(params[:url]) do |f|
charset = f.charset
f.read
end
replacements = {'1杯の'=>'a cup of','たくさん'=>'a lot'...}
regex = Regexp.new(replacements.keys.map { |x| Regexp.escape(x) }.join('|'))
#html = Nokogiri::HTML::DocumentFragment.parse(#html)
#html.traverse do |x|
if x.text?
x.content = x.content.gsub(regex, replacements)
end
end
end
EDIT2:
I have a problem, which all <a> tag replaced actually displays as a part of <p> tag and not linking. How I can escape <a> tag form <p>?
EDIT3:
This is what actually happening.
https://techacademy-leoreo2247.c9users.io/result/?utf8=%E2%9C%93&url=https%3A%2F%2Fringo-12.com%2F
inspect element on windowsfor more details
EDIT4:
Seems like it has different problem.
When I inspected element on my app, the code it should display likeWindowsis displaying like <a href="http://ejje.weblio.jp/content/Windows">Windows</a>.
I guess the code "Windows"is encoded to some other format.
Weblio's url format is like this: http://ejje.weblio.jp/{desired
english word}
Well, not quite: you can't have spaces in a url, so you have to do something extra to a phrase like "a lot of".
In rails, by default you use something called ERB to substitute values into a view. ERB allows you to write ruby code in your html and execute it. All your view files have the extension .html.erb--that is a hint that rails first runs your views through the ERB templating engine to produce the .html files. So, you need to read a tutorial on ERB syntax, such as this one. Here is an example of what ERB looks like:
<%
replacements = {
'1杯の'=>'a cup of',
'たくさん'=>'a lot',
'たくさんの'=>'a lot of',
}
replacements.values.each do |phrase| %>
<%= phrase %>
<% end %>
A <%= .... %> block inserts the results of the ruby code contained in the block into the html.
A <% ..... %> block is for ruby code that you want to be executed but whose results are not to be inserted into the html.
If you create your replacements in an action, e.g.:
def dostuff
#replacements = {
...
}
end
then your view would simply look like this:
<% #replacements.values.each do |phrase| %>
<%= phrase %>
<% end %>
Below is an example that demonstrates the use of the ERB templating engine outside of rails.
require 'erb'
require 'uri'
template = %q{ #=> Another way in ruby to write an opening single quote for a string.
<%
replacements = {
'1杯の'=>'a cup of',
'たくさん'=>'a lot',
'たくさんの'=>'a lot of',
}
replacements.values.each do |phrase| %>
<%= phrase %>
<% end %>
} #=>Closing single quote for the string
renderer = ERB.new(template)
result = renderer.result()
puts result
--output:--
a cup of
a lot
a lot of
In rails, you don't need to write the require or ERB.new() or renderer.result()--rails does all that automatically for an .html.erb file
Once you write a few views using ERB, and you feel comfortable with the basics, you can promptly forget about ERB and use one of the better templating languages, like slim, which is much prettier and easier to type than ERB. Rails has the means for incorporating any templating engine of your choice--but the default is ERB.
Response to comment:
def dostuff
replacements = {
'1杯の'=>'a cup of',
'たくさん'=>'a lot',
'たくさんの'=>'a lot of',
}
replacements.each do |key, val|
replacements[key] = %Q{#{val}}
end
require 'pp' #pretty print
pp replacements
end
Output in server window:
{"1杯の"=>"a cup of",
"たくさん"=>"a lot",
"たくさんの"=>"a lot of"}
Response to comment:
Your str is html escaped. If you need to literally display all the characters in the string <a>hello</a> on a web page, you must replace the angle brackets with what are called html entities, otherwise the browser will render the <a> tag as a link. The html entity for the < character is <, where lt stands for less than, as in a less than symbol in a mathematical comparison: if x < 4. Similarly, the html entity for the > character is >, as in greater than.
require 'cgi'
str = %q{Windows}
puts "<div>This is an html instruction site. First we will start with link tags.</div>"
puts "<div>Link tags look like this:</div>"
puts "<div>#{CGI.escapeHTML str}</div>"
str = %q{<a href="http://ejje.weblio.jp/content/Windows">Windows</a>}
html = CGI.unescapeHTML str
puts "<div>Here is what that link looks like when it is rendered by a browser:</div>"
puts "<div>#{html}</div>"
--output:--
<div>This is an html instruction site. First we will start with link tags.</div>
<div>Link tags look like this:</div>
<div><a href="http://ejje.weblio.jp/content/Windows">Windows</a></div>
<div>Here is what that link looks like when it is rendered by a browser:</div>
<div>Windows</div>
If you take the output of that program and open it in a browser, you will see:
This is an html instruction site. First we will start with link tags.
Link tags look like this:
Windows
Here is what that link looks like when it is rendered by a browser:
Windows

How can I convert erb to html?

Imagine in rails I have #template that is an instance of ActionTemplate::View.
Question is: How can I convert #template whose #template.source is <%= "hello from erb" %> to hello from erb?? thanks
Try this...
ERB.new(#template.source).result
ERB#new
Well... messing around with ActionView::Template.new outside of Rails is not really recommended. You need a ton of stuff setup beforehand (init and render)
If you do want to just use ERB, then go with this example
require 'erb'
x = 42
template = ERB.new <<-EOF
The value of x is: <%= x %>
EOF
puts template.result(binding)
And, you can use Kyle's answer to go from your template to ERB.

Rails 3 and HTML escape from within helpers

From one ERB view, I have this helper call:
<p><%=progress #object.progress %></p>
This is the helper method (I've simplified it):
def progress(value)
s = content_tag(:span, "pre:")
s += " <strong>#{value} %</strong>"
return s.html_safe
end
It seems that if you merge those two types of HTML strings, the latest part is not rendered properly. You'll see this:
pre: <strong>40 %</strong>
If you combine the strings like so:
def progress(value)
s = content_tag(:span, "pre:")
s += content_tag(:strong, " #{value} %")
return s.html_safe
end
everything work!
String returned from content_tag is marked as html_safe, when you add other unsafe string it's escaped before concat.
Here's a nice explanation on how the SafeBuffers (the class that does the html_safe magic) work: http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/
I think, it's happened, because string returned from content_tag marked as html_safe. So, if you try to add something to this string, it's automaticly escaped.
If you are using first example then add to_s for a type transformation.

Rails 3.0.2 Array#join HTML Safe?

I have a rails gem that uses a snippet like:
components = []
components << label_for(attribute)
components << ...
components << text_field(attribute)
return components.join
The gem worked fine in Rails 3.0.1, however it escapes (renders as text in a browser) all HTML after updating to Rails 3.0.2. What am I doing something wrong? Thanks.
As #sj26 points out, either use the rails built-in helper:
<%= safe_join(components) %>
Or use my rails_join gem to make Array#join html-safe aware, in which case your original code will work as is.
String#join isn't SafeBuffer-aware.
String#html_safe marks that you string is already HTML-escaped, preventing users sneaking bits of HTML into your pages. Check out this post by Yehuda Katz on SafeBuffer and why/how you should be using them.
If you have an array of String and SafeBuffer you want to concatenate, make sure you've run #html_safe on them all, or #concat them into a SafeBuffer like so:
['<one>', '<p>two</p>'.html_safe].inject ''.html_safe, &:concat
=> "<one><p>two</p>"
Rails has a built-in helper called safe_join which will do this for you.
http://makandra.com/notes/954-don-t-mix-array-join-and-string-html_safe
class Array
def html_safe_join(delimiter='')
''.html_safe.tap do |str|
each_with_index do |element, i|
str << delimiter if i > 0
str << element
end
end
end
end
[safe_string, unsafe_string].html_safe_join(' ')
# '<span>foo</span><span&t;bar</span>'
Strings are automatically HTML-escaped in Rails3. You need to change that last line to:
return components.join.html_safe
alternately, if editing the gem is too much hassle you can do it from the view:
<%= helper_name.html_safe %>
how about manual quoting?
<%= raw ['<div>', 'any', '</div>'].map{|val| h(val)}.join('<br />') %>

Don't escape html in ruby on rails

rails 3 seems to escape everything, including html. I have tried using raw() but it still escapes html. Is there a workaround? This is my helper that I am using (/helpers/application_helper.rb):
module ApplicationHelper
def good_time(status = true)
res = ""
if status == true
res << "Status is true, with a long message attached..."
else
res << "Status is false, with another long message"
end
end
end
I am calling the helper in my view using this code:
<%= raw(good_time(true)) %>
You can use .html_safe like this:
def good_time(status = true)
if status
"Status is true, with a long message attached...".html_safe
else
"Status is false, with another long message".html_safe
end
end
<%= good_time(true) %>
I ran into this same thing and discovered a safer solution than using html_safe, especially once you introduce strings which are dynamic.
First, the updated code:
def good_time(long_message1, long_message2, status = true)
html = "".html_safe
html << "Status is #{status}, "
if status
html << long_message1
else
html << long_message2
end
html
end
<%= good_time(true) %>
This escapes long_message content if it is unsafe, but leaves it unescaped if it is safe.
This allows "long message for success & such." to display properly, but also escapes "malicious message <script>alert('foo')</script>".
The explanation boils down to this -- 'foo'.html_safe returns an ActiveSupport::SafeBuffer which acts like a String in every way except one: When you append a String to a SafeBuffer (by calling + or <<), that other String is HTML-escaped before it is appended to the SafeBuffer. When you append another SafeBuffer to a SafeBuffer, no escaping will occur. Rails is rendering all of your views under the hood using SafeBuffers, so the updated method above ends up providing Rails with a SafeBuffer that we've controlled to perform escaping on the long_message "as-needed" rather than "always".
Now, the credit for this answer goes entirely to Henning Koch, and is explained in far more detail at Everything you know about html_safe is wrong -- my recap above attempts only to provide the essence of the explanation in the event that this link ever dies.

Resources