Rails link_to URL and HTML options with remote: true - ruby-on-rails

My current link
link_to title, params.merge(:sort => column, :direction => direction, :page => nil, :search => nil), {class: css_class}
I want to add remote: true into the mix but when I encapusulate it with the params.merge (i.e. {params.merge(), remote: true} I get a syntax error. In rails I need to encaps the url options and the html options separately right?
title and css_class are both variables inside the helper method this link is in

Better to use:
link_to 'title', params.merge(:sort => column, :direction => direction, :page => nil, :search => nil), {class: css_class}, :remote => true

You can do it like this:
link_to title, params.merge(:sort => column, :direction => direction, :page => nil, :search => nil), class: css_class, remote: true

In Rails 5, ActionController::Parameters no longer inherits from Hash, in an attempt to discourage people from using Hash-related methods on the request parameters without explicitly filtering them.
As part of this pull request, which was backported into Rails 5.1 and partially into Rails 5.0, an exception is raised if you try to call to_h on the parameters object without calling permit.
Calling merge on the original params object (params.merge(:sort => column, :direction => direction, :page => nil)) returns a new ActionController::Parameters object with the same permitted status (that is, permit has not been called on it). The link_to method then ends up calling to_h on that object, which raises the exception.
If you know which parameters should be allowed in the link, you can call permit with those listed.
params.permit(:param_1, :param_2).merge(:sort => column, :direction => direction, :page => nil)
# OR
params.merge(:sort => column, :direction => direction, :page => nil).permit(:param_1, :param_2, :sort, :direction, :page)
If you don't know which parameters could be included in the link, then it's possible to call request.parameters.merge(...) (as mentioned in this answer) or params.to_unsafe_h.merge(...). However, as pointed out in comments, this is a security risk when the result is passed to link_to, as a parameter like host would be interpreted as the actual host for the link instead of a query parameter. There are several other keys that also have special meaning in link_to (everything accepted by url_for, plus :method), so it's generally a risky approach.

Related

Argument Error - Whitelist and sanitize passed parameters to be secure

I just upgraded to Rails 5.0.1, and I'm running into a security warning:
ArgumentError in Categories#show
Showing /home/user/website/app/views/categories/show.html.erb where line #127 raised:
Attempting to generate a URL from non-sanitized request parameters! An attacker
can inject malicious data into the generated URL, such as changing the host.
Whitelist and sanitize passed parameters to be secure.
This is the offending code:
<%= link_to "Title", params.merge(:utf8 => params[:utf8], :search => params[:search], :x => "5", :y => ""), title:"Alphabetical" %>
I searched for this error and found a few similar problems, but they were either solved my running permit! instead of just permit (which is inapplicable in my situation) or the problem was a bug, which I hope isn't the case. I tried adding html_safe to my parameters but it didn't help.
Anyone know how I can sanitize my parameters to abide by Rails 5 security measures?
You can sanitize params as follows
<%= link_to "Title",
params.merge(
:utf8 => params[:utf8],
:search => params[:search],
:x => "5",
:y => "").permit(:utf8, :search, :x, :y),
title:"Alphabetical" %>

Ruby hash map with key that contains '-'

How can I add hash map element with a key that contains "-"?
Like this:
<%= button_to_function 'Cancel','cancelRemove("cancelEmail")', :data-dismiss=>'modal', :class=>'btn' %>
I get an error:
undefined local variable or method 'dismiss' for #<ActionView::Base:0x3482fed>
While :'data-dismiss' works, with data attributes you can also do
:data => { :dismiss => 'modal' }
Additional data-prefixed html attributes can be included in the same hash. So for example on another link you might do:
:data => { :remote => true, :method => 'delete' }
which would add to the link the html attributes data-remote="true" data-method="delete".
While the hash syntax is less compact for a single attribute, it's nice when you've got more than one html5 data attribute. And it's arguably a bit more rails-ish.
Just rename it to:
<%= button_to_function 'Cancel','cancelRemove("cancelEmail")', :'data-dismiss'=>'modal', :class=>'btn' %>

Rails: Setting class and data-tag of an HTML attribute with a single rails method

I'm currently working on a tour interface that guides new users around my site. I have a Tour model that has many TourStops, each of which contains information about a section of the site.
Basically, I'd like to write a function for the Tour model that -- when passed the number of a TourStop -- generates the correct class and data attribute for the HTML element it's attatched to. For example, I'd like
<%= link_to image_tag("new_button.png", tour.stop_data(1), :title => 'Add new asset'), new_asset_path %>
to call a function and return something like
def stop_data(order)
" :class => '#{tour_stops.find_by_order(order).name}',
:data => '{:order => order}'"
end
creating a link_to tag like:
<%= link_to image_tag("new_button.png", :class => 'tour_stop_1',
:data => {:order => 1}, :title => 'Add new asset'), new_asset_path %>
The above code doesn't work. Is something like this even possible? If not, what's a better approach I might take?
The image_tag accepts two parameters. A source, and a options Hash.
What you are trying to do is squeezing your return value from stop_data into this options Hash.
In order to get this to work, you first, need to return a Hash from stop_data, and second, make sure you pass only two arguments to image_tag - the source, and the options.
First:
def stop_data(order)
{
:class => tour_stops.find_by_order(order).name,
:data => { :order => order } # you may need order.to_json
}
end
Second:
link_to image_tag("new_button.png", tour.stop_data(1), :title => "Add new asset"), new_asset_path
This looks like it will work, but it won't, since your'e passing three parameters to image_tag.
When you do the following:
image_tag("new_button.png", :class => "tour_stop_1", :data => { :order => 1 }, :title => "Add new asset")
It looks like you're passing even 4 parameters to image_tag, but in fact they are only two. In Ruby, when the last parameter of a method is a Hash, you don't need to wrap the Hash key/value pairs in curly braces ({}), so the example above is essentially the same as
image_tag("new_button.png", { :class => "tour_stop_1", :data => { :order => 1 }, :title => "Add new asset" })
Now, to get your helper to work with image_tag, you need to merge the options, so they become only one Hash.
link_to image_tag("new_button.png", tour.stop_data(1).merge(:title => "Add new asset")), new_asset_path
Again, we're omitting the curly braces when calling merge, because it's only (and therefore last) parameter is a Hash. The outcome is the same as:
tour.stop_data(1).merge({ :title => "Add new asset" })

Ruby on Rails: link_to_remote javascript broken?

I have this
<%= link_to_remote "Next",
{:url => { :controller=>:objects,
:action=>:filter_recent,
:page=>#objects.next_page},
:with => "Form.serialize('filter')" },
:after => "alert('hello')"%>
I've tried :before, :after, :loading, :complete... none of them appear to be working... I know the button works, cause the table advances to the next page.
It looks like your arguments are incorrectly split up by the hash you wrapped them in.
Your :after JS snippet/callback is being passed to the html_options argument hash, not the options hash (where it would be used).
Change to the following:
<%= link_to_remote "Next",
:url => {
:controller=>:objects,
:action=>:filter_recent,
:page=>#objects.next_page
},
:with => "Form.serialize('filter')",
:after => "alert('hello')"%>

haml and no javascript? (rails: form_remote_tag and :with parameter)

i am trying to call a remote method to update page content via ajax/js.
either i am too tired already or haml is not parsing the following code correctly to send the value of the query field via prototype. any ideas?
- form_remote_tag(:url => {:controller => "search", :action => "line"},:with => "'query=' + $('query').value" ) do
%input{:type => 'text', :id => 'query'}
%input{:type => 'submit', :value => 'Search'}
thanks a lot!
t
Have you tried a
= form_remote_tag
instead of
- form_remote_tag
I'm new to HAML myself but I was under the impression that you'll need the form tag to be actually generated not just executed...
Try passing the :with as part of the options hash.
- form_remote_tag({ :url => {:controller => "search", :action => "line"}, :with => "'query=' + $('query').value" }) do
If that doesn't work, debug the problem: Look at the generated html. Is the text field with id query the only element in the page with that id? Is the js code correct? Use the Firebug console to ensure $('query').value returns whatever you've entered into the text field.
Still stuck? Add your generated html into your question so we can better help.
EDIT: Your query input tag does not have a name attribute. Without a name, the javascript helper code skips that field when serializing the form fields...also, you do not need the :with code.
%input{:type => 'text', :id => 'query', :name => 'query'}

Resources