Merging Params
by Jason on Feb 18, 2007
Update: This article is now about reinventing the wheel and showing you how easy it is to use the ruby language to accomplish your tasks. As robert pointed out in the comments, rails comes with the reverse merge method which you could call instead.
In this article, I'm going to show you how to have the params keep their state on your page in a very easy, concise manner. In most Rails pages you're only going to be changing the params for one object, possibly two. Conventionally, it's done like this:
<%= link_to 'Previous page', { :page => @post_pages.current.previous } %> |
But that becomes a problem if you want to have anything other than the page variable in your query string. A solution to that:
<%= link_to 'Previous page', { :page => @post_pages.current.previous }.merge(params.reject{|k,v| k=="page"}) %> |
Not so bad if you only have a couple of links. But I'm lazy and strive for minimalist code. Try throwing something like this in your application_helper
1 2 3 |
def params_merge(opts={}) opts.merge(params.reject{|k,v| opts.keys.include?(k)}) end |
And you can now do this:
<%= link_to 'Previous page', params_merge(:page => @post_pages.current.previous) %> |
You might be working with a RESTful application, in which case you may have some routes that are defined like this:
1 2 3 4 5 6 |
map.with_options :controller => 'surf_reports', :action => 'worldwide' do |worldwide| worldwide.global_news '/news', :category => 'news' worldwide.global_events '/events', :category => 'events' worldwide.global_latest '/latest', :category => 'latest' worldwide.global_location '/location', :category => 'location' end |
Now you know your route for global_news_path already has a category set. Let's say you want to link to that with seven other params. You can't just do global_news_path(params) because you might be in /events at the time, which already has the category set. Just like above, you could do something like this:
<%= link_to "News", global_news_path(params.reject{|k,v| k=="category"}) %> |
But this quickly becomes tedious. Try this in your application_helper:
1 2 3 |
def params_without(name) params.reject{|k,v| k==name} end |
And now you can do an easy link_to:
<%= link_to "News", global_news_path(params_without :category) %> |
Now you have some view code that's concise and pretty.
Update!
Duncan Beevers posted the following in the comments if you want something to work on all of your hashes. Thanks, Duncan!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Hash # lets through the keys in the argument # >> {:one => 1, :two => 2, :three => 3}.pass(:one) # => {:one=>1} def pass(*keys) tmp = self.clone tmp.delete_if {|k,v| ! keys.include?(k) } tmp end # blocks the keys in the arguments # >> {:one => 1, :two => 2, :three => 3}.block(:one) # => {:two=>2, :three=>3} def block(*keys) tmp = self.clone tmp.delete_if {|k,v| keys.include?(k) } tmp end end |
Comments
Sorry, comments are closed for this Post, but feel free to email Gregg & Jason with your input. We'd love to hear it.
RSS
iTunes
A bit more versatile. class Hash # lets through the keys in the argument # >> {:one => 1, :two => 2, :three => 3}.pass(:one) # => {:one=>1} def pass(*keys) tmp = self.clone tmp.delete_if {|k,v| ! keys.include?(k) } tmp end # blocks the keys in the arguments # >> {:one => 1, :two => 2, :three => 3}.block(:one) # => {:two=>2, :three=>3} def block(*keys) tmp = self.clone tmp.delete_if {|k,v| keys.include?(k) } tmp end end
For the first example, wouldn’t this be better?
<%= link_to ‘Previous page’, :overwrite_params => { :page => @post_pages.current.previous } %>
I guess I might be missing something here, but I use overwrite_params all the time for this sort of thing.
For the first case, that’s built into rails: http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/ReverseMerge.html#M000357, and I think for your second example it’d work too, since the right category is already in the hash and you’re trying to avoid overwriting it.
what robert said, reverse_merge comes from the Ruby Facets (gem) project…
facets.rubyforge.org
Hi!
As Peter said, overwrite_params in url_for will help. If you want to remove parameter set value in hash to nil. <%= link_to ‘users’, :overwrite_params => {:tags=>nil, :page=>nil} %>
def params_merge(opts={}) opts.merge(params.reject{|k,v| opts.keys.include?(k)}) end
or just
def params_merge(opts={}) params.clone.merge(opts) end