How can I preserve querystring values across requests in Ruby on Rails? - ruby-on-rails

I'm coming up against one of those moments working with Rails when I feel that there must be a better way than what I'm ending up with.
I have four querystring parameters that I want to preserve across requests through various parts of a rails app - different controllers and actions, some rendered via javascript - so that the user ends up at a URL with the same querystring parameters that they started with.
I'm finding it hard to believe that the best way is through hidden form fields and manually adding the params back in as part of a redirect_to, or using session vars for each - it just seems to un-rails like.
Does anyone know of a better way to manage this?
Thanks!

In cases like this, I'll often use a helper function to create urls based on the current set of params. In other words, I'd define this:
def merge_params(p={})
params.merge(p).delete_if{|k,v| v.blank?}
end
And then use it with url_for to create the urls for your forms and links. If you need to modify and of the params, just pass them into the merge:
# url for the current page
url_for(merge_params)
# url for a different action
url_for(merge_params(:controller => 'bar', :action => 'bar'))
# et cetera
url_for(merge_params(:action => 'pasta', :sauce => 'secret'))
This'll preserve existing params plus whatever overrides you merge in.

If you want to always preserve certain parameters accross every generated URL, you can implement a default_url_parameters method in your app.
This is poorly documented, but mentioned in the Rails 18n Guide
Implement in your application_controller.rb:
def default_url_options(*args)
super.merge(
:foo = params[:foo],
:bar = params[:bar]
)
end
Now every URL generated by Rails will include the current request's values for foo and bar (unless a url is being generated that specifies those specifically to be something else).

Please note this is not a rails solution but works for me in 80% cases. This is how I am able to achieve this with jQuery, obviously not the best approach. And I am unable to make it work past 2nd level of links:
function mergeGPS(){
var lat = getParam('lat');//localStorage.getItem('lat');
var lng = getParam('lng');//localStorage.getItem('lng');
if(lat == null || lng == null){return false;}
var links = jQuery("a");
jQuery.each(links, function(y, lnk){
//debugger;
var uri = jQuery(lnk).attr('href') + "?lat="+lat+"&lng="+lng;
jQuery(lnk).attr('href',uri);
//debugger;
})
}
function getParam(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
mergeGPS needs to be called on page load.

Related

Ruby on Rails - Passing parameters to set in url

this maybe is an usual and easy ask to answer but Im new in this.
I have my route and view asociated to my webtool ready and working, the route is like this "mysite.com/calculator". Its about some charts which change their values depends of choices in some levers (1 up to 4). I need to add the functionality to navigate at some url like "mysite.com/calculator/1113231" where the numbers are the choices preselected.
If i can do some like that I'll need to get the numbers and set the levers by JS i think.
Some advise and/or example of how to do this?
# everything in parenthesis after the path
# helper is added as a hash to the URL
# parameters
calculator_path(number1: 80085, number2: 58008)
...will translate to
# parameters added to a URL is noted
# by the '?' followed by each parameter
# assigned and it's value
/calculator?number1=80085&number2=58008
Then you can pull the params down in your controller
first_number = params[:number1]
second_number = params[:number2]
If you want to add a dynamic value, use a variable that gets assignment from somewhere else in your code....
calculator_path(x: #rdm_num_1, y: #rnd_num_2)

Ruby-on-rails: are there any other way to make page statefull except use session?

I am creating search form for model with some tabs, radiobuttons, dropbox, etc. I use index action for searching and sorting. Parameters for searching is persisted in params. It works while I stay in the same index view. But if I had to visit other page and then come back to search page again, params doesn't work and state is not persisted.
I know I can do it with session, but suppose I would have more search forms for another models and so all that params goes into session. Doesn't it make session messy?
So question is:
Is there other reasonable way to persist page state (but I don't want to put into database)
You might consider a class in your app to help manage data input/output, somewhat similar to ActiveRecord's database interface. You can store this data either in a session maybe memcache or redis server would be better. Here's an example.
class SearchParam
def initialize(id, model, query_string = nil)
#id = id
#model = model
#query_string = query_string
end
def save
# write #query_string to redis using `key`
end
def self.find(id, model)
instance = new(id, model)
instance.query_string = Redis.get(instance.key)
return instance
end
def query_string
#query_string
end
def query_string=(qstring)
#query_string=qstring
end
def key
"#{#id}-#{model}"
end
end
I forget the exact redis commands and syntax, but you'll get the idea if you've worked with it before - basic writing and reading. Memcache or session would also work. Then you can easily store the hash representation of the query string for a user/model combo and set your search params for use in the search form.
Easy to lookup based on a session id in the controller when they first get to the index page (if there are no params submitted)
#search_params = SearchParam.find(session.id, model).query_string
Or to save the new query_string when you return results based on the form submit in the index action
SearchParam.new(session.id, model, params).save
There is also localStorage (https://developer.mozilla.org/en/docs/Web/API/Window/localStorage) available in browser, but you have to use JS to access it.
Otherwise, I think it is okay to store it in session, unless you have like tens of different search forms.

Changing urls in ruby on rails depending on different conditions

I'm new to ruby on rails....I wanted to know if there is a way to change the URL displayed depending on the client's response. I mean... here's an example:
I'm making a project showing listings in various places...
Now in general I have a home page, a search page, and a detail page for listings. So, respective URLs are officespace/home, officespace/search?conditions, officespace/detailpage?id=(controller-officespace)[&Conditions eg.---price,size,place,type...]
So, every time the client makes a request for search, the same URL is shown, of course with the given conditions.
Now I want that if the client asks for only the place and mentions nothing about size, price, etc., the url should be /listing/location_name.
If he mentions other conditions, then it'll be listing/(office_type)/size(x sq feet)_office_for_rent_in_locationname)
B.t.w. (I already have a controller named listings and its purpose is something else.)
And so on ........... Actually, I want to change URLs for a number of things. Anyway, please help me. And please don't refer me to the manuals. I've already read them and they didn't give any direct help.
This is an interesting routing challenge. Essentially, your goal is to create a special expression that will match the kinds of URL's you want to display in the user's browser. These expressions will be used in match formulas in config/routes.rb. Then, you'll need to make sure the form actions and links on relevant search pages link to those specialized URL's and NOT the default pages. Here's an example to get started:
routes.rb
match "/listing/:officeType/size/:squarefeet/office_for/:saleOrRent/in/:locationName" => "searches#index"
match "/listing/*locationName" => "searches#index"
resources :searches
Since you explicitly mentioned that your listings controller is for something else, I just named our new controller searches. Inside the code for the index method for this controller, you have to decide how you want to collect the relevant data to pass along to your view. Everything marked with a : in the match expressions above will be passed to the controller in the params hash as if it were an HTTP GET query string parameter. Thus we can do the following:
searches_controller.rb
def index
if params[:squarefeet] && params[:officeType] && params[:locationName]
#listings = Listing.where("squarefeet >= ?", params[:squarefeet].to_i).
where(:officeType => params[:officeType],
:locationName => params[:locationName])
elsif params[:locationName]
#listings = Listing.where(:locationName => params[:locationName])
else
#listings = Listing.all
end
end
And to send the user to one of those links:
views/searches/index.html.erb
<%= link_to "Click here for a great office!", "/listing/corporate/size/3200/office_for/rent/in/Dallas" %>
The above example would only work if your Listing model is set up exactly the same way as my arbitrary guess, but hopefully you can work from there to figure out what your code needs to look like. Note that I wasn't able to get the underscores in there. The routes only match segments separated by slashes as far as I can tell. Keep working on it and you may find a way past that.

Figuring out facebook iFrame clients

I am in the process of building a facebook app that works through iFrame with Ruby On Rails.
My App does serve multiple clients, web, mobile, and facebook. And depending the type of client the UI renders different kind of views.
When the user connects to my app using the facebook page tab, I do get enough information (in params collection) to identify the user came from facebook. Based on that I can customize the views to fit into the iFrame.
But for subsequent requests, because they happens through iframe, there is nothing that tells this is a facebook request (as far as I can tell unless there is something in the headers which I dont know of).
I tried setting a cookie during the first request and that worked great. But the problem is when the user requested my app directly from another browser tab (not through facebook) the cookie was still present and the user ended up seeing the facebook(ised) UI, instead of Normal UI.
Anyone has a solution to this?
I recommend using a different route for Facebook tabs. So if your regular URLs look like this:
/foobar
Then you may want to use something like this for Facebook, by adding the "/fb" prefix (or similar) to your Facebook app's tab or canvas URL:
/fb/foobar
In your routes.rb, you can then pass a parameter to indicate that the user is viewing the page on Facebook, such as "channel=facebook" or "facebook=true".
Your routes.rb might look something like this:
scope 'fb', :channel => 'facebook' do
# ... your routes ...
end
scope :channel => 'web' do
# ... your routes ...
end
With these routes, each request originating from Facebook will automatically have a "channel=facebook" parameter. Of course, you'd still be responsible for making sure to generate the appropriate URLs within your app. You could add :as => 'facebook' and :as => web to the scopes above and use this to generate URLs using dedicated helpers (e.g. facebook_foobar_url, web_foobar_url). But the best way to do this depends a lot on the complexity of your app.
Alternatively, you could also use default_url_options in your controller to add a "channel=facebook" or "facebook=true" parameter to every generated URL if you detect that the current request originated from Facebook (either from the existence of a signed_request or from channel/facebook param you added). Note that this method is deprecated in Rails 3, and I'm not quite sure what (if any) the official replacement is in Rails 3.1 or 3.2.
I needed to solve this with the least amount of intrusion into the existing code.
And am posting my solution here for the benefit of anyone looking to solve similar problem.
I have a unique layout for facebook and this was invoked during the first request (like I mentioned above, the initial request posted from facebook tab has facebook params).
To make sure there was facebook param for subsequent requests, inside the facebook layout, I bound all the form submission and anchor click events to a method that would add a hidden form element and query string param respectively.
this is how the client side code looks like:
<script type="text/javascript">
$(document).ready(function() {
$('form').submit(function(e) {
$('<input>').attr({
type: 'hidden',
id: 'fb_param',
name: 'fb_param',
value: 'true'
}).appendTo($(this));
return true;
});
$('a').click(function(e) {
var newUrl = $(this).attr("href");
if (newUrl.indexOf("?") != -1) {
newUrl = newUrl + '&fb_param=true';
} else {
newUrl = newUrl + '?fb_param=true';
}
$(this).attr("href", newUrl);
return true;
});
});
</script>
Now to handle the server side redirects (typical when you update a resource etc), needed to extend the redirect_to method like so:
/app/conntrollers/application_controller.rb
class ApplicationController < ActionController::Base
def redirect_to(options = {}, response_status = {})
if params[:fb_param] == 'true'
if options
if options.is_a?(Hash)
options["fb_param"] = "true"
else
if options.include? "?"
options = "#{options}&fb_param=true"
else
options = "#{options}?fb_param=true"
end
end
end
end
super
end
end

Append query string to request.request_uri without knowing if there was a query string

When a user arrives to my site on an iPhone I want to present them with a page with 2 options:
1) Go get our app or
2) Continue on to the requested URL
I do this by checking the user agent in my application controller and rendering a view.
For option 2 to work properly on that view I want the link to preserve the original URL and append a query string parameter fullsite=1. I look for that query string parameter param in a before_filter in my application controller and set a cookie so that the user is not prompted again.
What is the cleanest way to append a query string parameter to a request.request_uri knowing that that request url may or may not have had a query string to start with? I know it is straight forward to check if the request had parameters but I'm wondering if there is a helper to append a parameter cleanly in either case (no params or with existing params) without evaluating things on my own. Seeking the correct "railsy" way to do this.
Probably the best way to do this is to add your flag to the params hash, which will already have all the other information you need to build that link (presuming you catch iPhones and render in a before_filter), and then build your link from the modified params hash:
In your before_filter:
params[:fullsite] = 1
And then in your view:
<%= link_to('Continue to the Full Site', params) %>
This way Rails gets to do all the messy work of escaping and appending your query strings - both :fullsite and whatever the original query string contained.
If you're just using :fullsite as a way to avoid redirecting iPhones through your iPhone portal multiple times, though, it seems like either of these approaches would work better:
Set :fulltext in the session instead, or
Only redirect users if they're on iPhones AND request.referer is something other than your site.
Hope this helps!

Resources