Routes and query strings magic in Rails - ruby-on-rails

So, I recently saw this code:
<%= link_to "Index", plays_path(id: 1) %>
1) I did not know that the index path or the plays_path took arguments. The URL doesn't have any named parameters and so I didn't think you could.
2) I read about query strings but the docs aren't great.
ActionView UrlHelper
There is no usage of the word query strings. How can you just pass any hash to a path in rails and have the params be available in the following controller? What is going on here?

You're exactly correct. Any parameters you pass in to URL / path helpers (which are not part of a named route, such as :id) will be available as parameters within your controller.
The Rails Routing Guide talks a bit about parameters and is always a good resource for how to best utilize additional parameters passed through a _url or _path helper.

All of the path helpers take a hash of arguments on the end. They're passed as extra parameters in the query string. For instance: plays_path(id: 1) will (likely) generate /plays?id=1. You can append a whole mess of them on the end as in user_path(user, active_user: 5, secure: 1, other_thing: 'happy') where user is an instance of User with id 30, would generate /user/30?active_user=5&secure=1&other_thing=happy. Most of the time these are GET parameters.

Related

Path helper changing "/search?option=..." to "/search.option=..." when anchor is specified

I have a seemingly simple case with Rails' path helpers. My route is straightforward:
get 'search', to: 'search#search_results'
$ rails routes
...
search GET /search(.:format) search#search_results
I need to inject an option=graph parameter into the query string on the current page. This works as expected:
<%= link_to "Graphs", search_path(request.query_parameters.merge({:option => "graphs"})) %>
Giving a link like this:
http://localhost:3000/search?body=&commit=Search&feedback=&option=graphs&reason=&site=&title=&user_rep_direction=%3E%3D&user_reputation=0&username=&utf8=%E2%9C%93&why=foo
However, when I specify the anchor parameter, something weird happens:
search_path(request.query_parameters.merge({:option => "graphs"}), anchor: "graph-1")
http://localhost:3000/search.body=&commit=Search&feedback=&option=graphs&reason=&site=&title=&user_rep_direction=%253E%253D&user_reputation=0&username=&utf8=%25E2%259C%2593&why=foo#graph-1
The anchor is added as expected, but the ? to start the query string turns into a . - which irritates the controller because it's an unrecognized format.
Why is this happening, and how can I fix it?
Running Rails 5 beta 4 on Ruby 2.2.2
After some testing it turns out that you should include the anchor when merging the params:
search_path(request.query_parameters.to_h.merge({option: "graphs", anchor: "graph-1")})
Explanation: your approach, i.e.:
search_path(request.query_parameters.merge({:option => "graphs"}), anchor: "graph-1")
actually passes two separate hashes as arguments to the search_path helper instead of just one. However, in the helper definition code, only the last hash argument is taken as the real options parameter to the helper. Other arguments are processed differently.
In the end, the anchor argument gets understood by the helper as the format specifier (I found this out by debugging the path construction in the helper but am unable to point to a precise place in the source code) and that is why the resulting path contains a dot . after the resource name.
Update: Note, that the query_parameters must also be converted to a hash, as normally they are of the ActionController::Parameters and are also processed differently in the path helper. After unifying all params and the anchor to a Hash class, the path should be correct.

Passing a list of same parameters with a different value

So basically I was creating some client methods that would create a path to hit an external service.
Also, I am using addressable gem.
Here is an example:
def get_member(ord_id, member_id)
path = '/organizations/{ord_id}/people/{member_id}'
hash = get(path, member_id: member_id, org_id: ord_id)
{ Member.custom_deserialize_method(hash) }
end
This works if the path is simple as above.
Now I want to write a method for a different path which does a bulk look up like this:
organizations/ab9176be/members/bulk?memberId=8e936891&memberId=b71c4f1e (This is not a web url. Its a service end point)
The above method can have multiple memberId params.
I know addressable has an expand method and ruby has a to_param method.
But I do not not know if that would help in my situation. I would appreciate some help here.
Route Globbing
I'm sure if this will help, but considering you've had no responses, I felt I'd be best posting
I learnt about route globbing a few weeks back. Bascially this allows you to define routes like this:
get 'books/*section/:title', to: 'books#show'
Would match books/some/section/last-words-a-memoir with
params[:section] equals 'some/section', and params[:title] equals
'last-words-a-memoir'.
Although not directly related to the solution for your question, it may help you appreciate how Rails processes some of the routes in the app. Essentially, it means you can add as many "wildcard" routes as you need, with the various elements of the route being compiled in the params hash

Custom Rails 4 Routes containing "/"

I have a method that generates a random url ending and a get path that looks like
/path/var1/var2
The only problem is that some of those generated values for var2 have a "/" in them. So if var 2 is "h4rd/erw" rails reads it as
/path/var1/h4rd/erw
or
/path/var1/var2/var3
rails thinks that this is another parameter of the route and i keep get the error
No route matches.
I have thought of setting up the generated value for var2 to not include "/"s or possibly putting a wildcard in the route if that's possible like /path/var1/*. What would be the best solution for this?
You can use route globbing:
get '/path/var1/*var2', to: 'controller#action'
Let's say the request went to /path/var1/h4rd/erw. Then in the controller action, the value of params[:var2] would be h4rd/erw.
I would make sure that the values are always escaped.
string = "my/string"
=> "my/string"
CGI.escape(string)
=> "my%2Fstring"
So your url will be like
/path/var1/h4rd%2Ferw
and it will go to the right controller with the right variables set.
Rails will automatically unescape the values of parameters in the controller "params" variable, so by the time you're dealing with the value in the controller the slash will be back in the string.
The only remaining question is when to do the escaping. If you pass the value as an argument to a path helper then rails should escape it automatically for you, like so:
link_to "Here", my_route_path(:foo => "bar")

Rails: The easiest way to differ path parameter from query string parameter?

Rails router gives us an easy way to define optional path parameters, like this:
# config/routes.rb
scope "(:locale)", locale: /ru|de|fr/ do
resources :books
end
Thus we can access /users path and get the default locale, or /ru/books and get the locale in params[:locale].
But with the same setup we can also call the page /books?locale=ru and get the same effect (both path parameters and query string parameters are treated equally and put in the params hash). If locale is processed in a global before_action as Rails i18n guide suggests we can even set locale for the pages that are not supposed to be localized.
So my question is what is the simplest and cleanest way differ path parameters from query string parameters (with the goal to ignore certain query string parameters)?
Answering my own question:
There is a method ActionDispatch::Request#query_parameters. It returns only the parameters set via query string.
There are also method path_parameters and symbolized_path_parameters. It is obvious they return parameters derived from path (including controller and action). They can be called on request inside a controller action. (They are not documented under ActionDispatch::Request, this is why I missed them initially.)
Rails 5 (edit Jan 9, 2017): As of Rails 5 method symbolized_path_parameters was removed. Method path_parameters is now documented.

Using parameter[:user_id] ,params[:user_id], params["userid"] in Rails?

While studying a Rails application I saw statements like:
parameter[:user_id]
params[:user_id]
params["userid"]
Can anyone tell me if there any major difference between them? Or, are all
fetching parameters only and can I use them interchangeably?
parameter[:user_id]
I don't think this is something official. However there's a parameters method on the current request object. See request.parameters in a controller action.
params[:user_id]
Using the params[:user_id] is the same as calling request.parameters[:user_id]. Also params[:user_id] is the same as params["user_id"]. See HashWithIndifferentAccess.
I am not sure if that's just a typo on your part, but params[:user_id] and params["userid"] are not the same, even with HashWithIndifferentAccess. The _ won't just go away so they can hold different values.
No, you need to look at the context in which each one is used. params is the normal parameter hash made available to methods in your controllers. It contains the parameters passed in as part of the request as GET or POST params.
So, given the following URL:
http://www.testsite.org/some_resource?user_id=13
The params[:user_id] would contain the value 13.
If the URL instead was:
http://www.testsite.org/some_resource?userid=13
You would need to use params[:userid] to get the value. So it all comes down to the way the URLs are made for the different controllers.
There's a third way, where you can map parts of the URL itself to params in the routes into your application. See config/routes.rb in your application. For instance with the following route:
match '/user/:user_id' => 'users#show'
You could pass in an URL like this:
http://www.testsite.org/user/13
And in your UsersController.show method you could access the user id by using params[:user_id] like normal.
The parameter hash is most likely a local copy of or reference to the params hash in a helper method or something.
params and parameters are the same. They return both GET and POST parameters in a single hash.
There is no such a thing as parameter.
ref: rails api

Resources