Why are ampersands escaped when generating url with link_to? - ruby-on-rails-3

Here is my simple rails 3 code :
<%= link_to "link", gateway_index_url(developer:#item.developer.api_key, tracker:"email", url:#product.url) %>
And the result is :
<a href="/gateway?developer=abcde&tracker=email&url=http%3A%2F%2Fwww.bla.fr%2FproductA" >link</a>
The problem is that & are rewritten in &. I can't figure how to prevent escaping, as :escape => false doesn't exist in Rails 3

Update: So here's the source
def link_to(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
link_to(capture(&block), options, html_options)
else
name = args[0]
options = args[1] || {}
html_options = args[2]
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
href = html_options['href']
tag_options = tag_options(html_options)
href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
"<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a>".html_safe
end
end
As we can see, from the source, this behavior is by design.
You can try one of two solutions, I haven't tried them but they should work
1.) Try placing the call to gateway inside of a call to #raw:
<%= link_to "link", raw(gateway_index_url(developer: #item.developer.api_key, tracker:"email", url:#product.url)) %>
That may solve your specific problem, an the second approach, while a bit more brute-force should also work...
2.) If you want to convert it (the whole href) back you can... use CGI::unescape_html:
<%= CGI::unescape_html(link_to "link", gateway_index_url(developer: #item.developer.api_key, tracker:"email", url:#product.url)) %>
Good luck, hopefully this helps.
Update 2: Fixed call to cgi unescape, was using "." when it should be "::" and formatting fix. Forgot to indent example for #1

Rory O'Kane is spot on. The answer to "Why are ampersands escaped when generating url with link_to?" is that is the correct way to separate params in a url.
Is there a problem with the url the way it is?
If so, could you elaborate on the problem?
You may be able to prevent escaping the url by using raw on the entire url like so:
<%= link_to "link", raw(gateway_index_url(developer:#item.developer.api_key, tracker:"email", url:#product.url)) %>

Related

Learning to Search in Rails

I'm trying to create a search form in my rails application. I've looked up various solutions but they make little sense to me.
I'm getting the following error when I run a search through a form in my rails app. Right now my concern (other than the error) is my instance variable #computers in my index action. I'm pretty sure it's not 'the rails way' to get a search done properly and would love some advice.
Error
undefined method `%' for #<Array:0x5780460>
Parameters after Search
http://localhost:3000/computers?utf8=%E2%9C%93&direction=&sort=&search=bob
Search Form
<%= form_tag computers_path, method: "get" do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Go", name: nil, class: "btn btn-primary" %>
<% end %>
Call to Method
def index
#computers = Computer.where(school_id: current_user.school_id).search(params[:search]).category(params[:category]).order(sort_column + " " + sort_direction)
end
Method
def Computer.search(search)
if search
search = search.downcase
params = []
values = {}
column_names.each do |c|
params << "#{c} LIKE #{c.to_sym}"
values[c.to_sym] = search
end
params.join (' OR ')
where(params,values)
else
all
end
end
You've got the right idea, but invoking the .join method does not change the object on which it is called, it merely returns a string representation. You need to store the return in a variable, something like this: paramsStr = params.join(' OR '). Then simply pass paramsStr to the where clause.
Ultimately, that is what is causing your unidentified method % for Array .... error; this version of the where method is expecting the first parameter to be a string. Check out this documentation, the part about placeholder conditions.
Hope that helps.

including rails object in meta tags keywords

I'd like to include a rails object in my keywords as well as straight text but the code is clearly not the right way to do it...how can I do this?
set_meta_tags :keywords => %w[keyword1 keyword2 #{params[:hospital]}]
You might want to have a look at two plug-ins for including rails object in meta tags:
Meta Magic: https://github.com/lassebunk/metamagic
Head Liner: https://github.com/mokolabs/headliner
Edit: For Meta tag gem
What I usually do is write a meta helper that I simply stick in my ApplicationHelper, that looks like this:
def meta(field = nil, list = [])
field = field.to_s
#meta ||= {
'robots' => ['all'],
'copyright' => ['My Copyright'],
'content-language' => ['en'],
'title' => [],
'keywords' => []
}
if field.present?
#meta[field] ||= []
case list.class
when Array then
#meta[field] += list
when String then
#meta[field] += [list]
else
#meta[field] += [list]
end
case field
when 'description' then
content = truncate(strip_tags(h(#meta[field].join(', '))), :length => 255)
else
content = #meta[field].join(', ')
end
return raw(%(<meta #{att}="#{h(field)}" content="#{h(content)}"/>))
else
tags = ''
#meta.each do |field, list|
tags += meta(field)+"\n"
end
return tags.rstrip
end
end
You can simply set meta tags in your views, by adding a call to meta() in it. So in an articles/show.html.erb you might add this to the top of your view:
<% meta(:title, #article.title) %>
And in your layouts, you add it without any parameters, so it'll spit out the meta tags.
<%= meta %>
Or have it output an individual tag:
<%= meta(:title) %>
I bet you there's more elegant solutions, though.
But if you were looking for something already implemented in Rails you're out of luck.
Thanks.
Try this in your view as it worked for me (using meta-tags gem):
<% keywords [[#modelname.keyword1], [#modelname.keyword2]] %>
and you cad additional keywords in text format by adding them within the ruby in the following format ['keyword3']

Access a query in Ruby on Rails

I have in my controller this:
#itemsok = Search.where("first_item_id = ?", params["3"])
This is sopposed to be a query in the search table of the database asking for all the searches that have a first_item_id = 3 ...
Question 1 .- The syntax is I found it in http://guides.rubyonrails.org/active_record_querying.html but im not sure if im using it right?
Ok the question 2 is, I have this on the controller, is it ok to have querys in the controller?
In the view im printing the variable <%= #itemsok %> and all I get is a
ActiveRecord::Relation:0x007fd3d3e894d8
Any suggestions?
Thanks in advance.
ActiveRecord 3 lets you chain relations together so you can do something like this:
#itemsok = Search.where("first_item_id = ?", params["3"]).where("foo = ?", "bar")
The where() function returns an ActiveRecord::Relation. Generally this isn't a problem, since if you use the object it'll automatically run the query and return the results on the object so you'll get the database objects. AR doesn't run the query until it's actually needed.
Where will return a list of items (Array), so if you're just debugging, change your view to this:
<%= debug #itemsok.to_a %>
You seem to be constructing the query wrong way.
If you want to search for records with first_item_id = 3, you should do:
Search.where("first_item_id = ?", 3)
This will return an array of matching records, something you can't easily print with <%= #itemsok %>. You should iterate over the elements and print each one:
<% #itemsok.each do |item| %>
<%= item.name %>
<% end %>
I'd also suggest defining to_s method for the objects you want to print.
class Search
def to_s
name
end
end
Then you can simply print the object and to_s method will be automatically called for you:
<% #itemsok.each do |item| %>
<%= item %>
<% end %>
The right way to do is to define a namedscope in the model and then use it in the controller.
Something similar to this :
class Search < ActiveRecord::Base
named_scope:item_ok,lambda {|*args|{:conditions=>["item_id >= ?", args.first]}}
end
and then call the namedscope from the controller like this :
#itemsok = Search.item_ok(params[:value])

Rails 3 custom validation: Highlighting offending fields

I'm writing my first custom rails validation, and would like to tag the offending class with an html "error" class if they return false - I can't quite figure out how to do it. Relevant validation code below - any help appreciated.
(If it makes a difference, I'm using jQuery)
validates_each :shop do |record, attr, value|
shopvar = record.shops.map{ |s| s.email.downcase.strip }
if shopvar.count != shopvar.uniq.count
record.errors.add(attr, 'has the same email address entered more than once')
#record.errors[attr] << "You have entered this shop in the form twice"
end
end
So in your form you'd have something like this for an input field
<%= form.text_field :title %>
Since errors is a hash you could use the "include?" method like so...
errors.include?(:title)
This tells you that there's something wrong with this field. Now all you need to do is style it.
Whack on a ternary operator asi...
<% css_class = errors.include?(:title) ? "highlight_error_class" : "no_problem_class" %>
<%= form.text_field :title, :class => css_class %>
Done.

Giving 'TemplateError' can't convert String into Integer

I recently transfered my app from Rails2 to Rails3.
The code in 'app/views/distribution/index.html.erb' is like :-
<div style="padding-bottom:10px; padding-left:0px;float:left;display:<%= (!session[:album][#artist.id.to_s].empty? && !session[:album][#artist.id.to_s].nil?)?'block' : 'none' %>" id = "make_payment_enabled">
<%= link_to 'Make Payments',{:action => 'pay', :album=>#album.id}, :class => "button" %>
</div>
It's giving me TemplateError on line :-
<div style="padding-bottom:10px; padding-left:0px;float:left;display:<%= (!session[:album][#artist.id.to_s].empty? && !session[:album][#artist.id.to_s].nil?)?'block' : 'none' %>" id = "make_payment_enabled">
How to resolve the problem ?
Solution 1: In the ERB tag, try putting spaces around the 'or' question mark, i.e. ....nil?) ? 'block....
Solution Better: Do step one, then put that code in a helper. Will really help to clean up your views.
UPDATE:
A few other tips: you will want to switch the order of the conditions, because you will want to see if the value is nil before checking if it's an empty string.
Calling obj.blank? is the equivalent of calling obj.nil? && obj.empty?, so that could make the code a bit shorter. Even better, obj.present? is the same as !obj.blank?.
Therefore, that line could be simplified to:
session[:album][#artist.id.to_s].present? ? 'block' : 'none'
Happy Rails-ing!