Rails - Extract HTTP Verb From Named Route - ruby-on-rails

Is there a way in Rails to extract the HTTP verb(s) associated with a route? For example, given a route like this:
match 'users', to: 'users#show', via: [:get, :post]
Can I achieve something like this?
users_path.respond_to?(:get) (obviously #respond_to is not the right method)
The closest I've managed to come is by doing the following, but it doesn't really seem satisfactory.
Rails.application.routes.routes.named_routes["users"].constraints[:request_method] # => /^GET$/
For context, I have an action that sets a cookie and then does a redirect_to :back, but this action is available globally across the entire site (it's in the footer). So, if a user happens to be in a flow, and one of those routes only accepts POSTs, the redirect fails because the request issued is a GET.

The request object is available to your controller. The following methods are available to determine the type of HTTP request:
if request.get?
# request is a GET request
if request.post?
# request is a POST request
There are similar methods for other HTTP request verbs, including PUT and DELETE.
UPDATE:
Per the update to the question, the following code can be implemented within your controller to yield the constrained verbs on any named route as a pipe-delimited string:
Rails.application.routes.named_routes["users"].verb
#=> "GET|POST"
Accordingly, you can split the string to retrieve an array of each of the HTTP methods specified in the route's constraints:
methods_string = Rails.application.routes.named_routes["users"].verb
#=> "GET|POST"
methods_array = methods_string.split('|')
#=> ["GET", "POST"]
methods_array[0]
#=> "GET"
methods_array[1]
#=> "POST"

for someone looking with it in future you can use
env["REQUEST_METHOD"]
to get HTTP verb of specific action

Related

How does the params method work?

I have been trying to figure out how the params method works and I'm a bit stuck on the process.
For example, if a user clicks on a certain blog post in the index page, I guess that the link_to method calls the Post controller and the show action along with its block #post = Post.find(params[:id]) and then goes to the database to find the post and the view displays it.
So my missing link seems to be when is the post id passed into the params method?
Because the others already explained about params, I'm just going to answer directly a question of yours:
when is the post id passed into the params method
I think it's best explained with an example; see below:
say that you clicked a link:
/posts/1/?param1=somevalue1&param2=somevalue2
The Rails server receives this request that a client wants to view this GET /posts/1/?param1=somevalue1&param2=somevalue2 address.
To determine how the Rails server will respond, the server will first go to your routes.rb and find the matching controller-action that will handle this request:
# let's say your routes.rb contain this line
# resources :posts
# resources :posts above actually contains MANY routes. One of them is below
# For sake of example, I commented above code, and I only want you to focus on this route:
get '/posts/:id', to: 'posts#show'
From above notice that there is this :id, Rails will automatically set params[:id] to the value of this :id. This is the answer to your question where params[:id] comes from.
It doesn't have to be :id; you can name it whatever you want. You can even have multiple URL params like so (just an example):
get /users/:user_id/posts/:id which will automatically set the value on params[:user_id] and params[:id] respectively.
In addition to this URL params like :id, Rails also injects values to params[:controller] and params[:action] automatically from the routes. Say from the example above, get '/posts/:id', to: 'posts#show', this will set params[:controller] to 'posts', and params[:action] to 'show'.
params values also comes from other sources like the "Query string" as described by Mayur, and also comes from the body of the request, like when you submit a form (the form values are set within the body part of the request) and like when you have JSON requests, which all of these are automatically parsed by Rails for your convenience, so you could just simply access params and get the values as you need them.
Params are hashes in ruby with Indifferent access which means,
hsh = {"a" => 1, "b" => 2}
Consider this hsh as params returned from a POST request from browser, it's a key value pair with keys as string. Since it's a params so the values can be accessed as
hsh["a"]
=> 1
hsh [:a]
=> 1
params are formed on the client where the interface load, consider a form which has a submit button. When you press submit, the data filled in form or any hidden textboxes are formed into a hash and passed across the request. This when received on server end will be called as params or request params.
For Get requests: data send across the url will be read as params on backend.
GET: http://www.abx.com?user=admin
params on backend: {"user" => "admin"}
This will be displayed in rails server logs
For Put request: data send across the body will be called params.
PUT: http://www.abx.com
data: {"user" => "admin"} Client side
params on backend: {"user" => "admin"}
This will be displayed in rails server logs
How does the params method work?
The params come from the user's browser when they request the page. For an HTTP GET request, which is the most common, the params are encoded in the URL. For example, if a user's browser requested
http://www.example.com/?post=1&comment=demo
then params[:post] would be "1" and params[:comment] would be "demo".
In HTTP/HTML, the params are really just a series of key-value pairs where the key and the value are strings, but Ruby on Rails has a special syntax for making the params be a hash with hashes or array or strings inside.
It might look like this:
{"post"=>"1", "comment"=>"demo"}
Link to Rails Guides on params: guides

Incorrectly routing /index to /show in Rails

There must be an obvious answer to this, but I'm at a loss.
I have a Rails app which tracks sites. For whatever reason, localhost:3000/sites leads to my index page. However, localhost:3000/sites/index leads to my show page.
Why is this?
Below is the routes file:
Rails.application.routes.draw do
resources :sites
Below is the sites controller:
class SitesController < ApplicationController
def index
end
def show
end
end
In views/sites, there is the index.html.erb and show.html.erb files. The former displays at /sites, the latter displays at /sites/index (and /sites/show as one would expect).
Thanks for any help!
UPDATE:
When I rake routes, I get:
Prefix Verb URI Pattern Controller#Action
sites GET /sites(.:format) sites#index
POST /sites(.:format) sites#create
new_site GET /sites/new(.:format) sites#new
edit_site GET /sites/:id/edit(.:format) sites#edit
site GET /sites/:id(.:format) sites#show
PATCH /sites/:id(.:format) sites#update
PUT /sites/:id(.:format) sites#update
DELETE /sites/:id(.:format) sites#destroy
These routes are what one would expect, but I guess I'm just surprised that sites/index presumes index is an :id and therefore routes the request to show.
I suppose I had never explicitly encountered that behavior before.
There are 7 basic actions in a controller by default, only 2 of them are matched by name in the url - (new and edit). These 2 are HTML constructs, they are just a way of rendering a form.
The other 5 (index, show, update, create, destroy) are the more basic routes and do the work to display and modify resources. They are referenced by only two url patterns (the two patterns you mentioned above - eg '/sites' and '/sites/:id'). They are differentiated by the method that goes along with the request: (Patch, Post, Get, Delete). So, "/sites" would be used for create and index. "/sites/:id" would be used for show, destroy, and update.
The action in the controller is not referenced by the url exactly - the url pattern and the request method together are used to call the associated controller method.
In the request "/sites/index", as the string "index" is in the url, the only route that matches is one that has a variable after "/sites/". :id is just a variable, not necessarily an integer. Since the request was a GET, the first (and only) route match is sites#show. "index" will be the value of params[:id] passed into the show action.
This is the way routing works in Rails. Take a look at Rails routing to get a better understanding of how this works. Especially the
Rails resource routing section.
Generally when you setup a resource route as you did the urls would be as follows:
example.com/sites #=> index page
example.com/sites/:id #=> show page. A specific site, where :id would be the unique identifier
# Here's an example URL with a specific site
example.com/sites/stackoverflow
Your index page is at: localhost:3000/sites
Navigate to localhost:3000/rails/info/routes to see your full app routes in development mode.

What is the right Rails route for passing in a different parameter than id, to return a JSON User object? Controller method provided

EDIT: This is Rails 4
Rails code in the users_controller.rb file
def showobjectdata
#users = User.all
#user = User.find_by(:username => params[:username])
render :json => #user
end
I have been trying lots of routes, but (add the "localhost" part to the beginning of this URL) /users/showobjectdata/existingusername in my browser
returns null.
Please Note: I am able to render JSON data about all users or a specific user, if I look up the user some other way than passing in a parameter which is not an id in the browser's URL field. Like in the controller method I can specifically look up a user by a specific email address. And users/show/:id renders the JSON user data of that id, because I have defined the show controller method to render JSON user data (for now).
Here is an example of a route I tried in my routes.rb file:
match 'users/showobjectdata/:username', to: 'users#showobjectdata', via: [:get, :post]
I tried various combinations with plain GET, plain POST, nested parentheses, etc. I always get null except for plain POST which doesn't work.
Try this
match 'users/showobjectdata/:username', to: 'users#showobjectdata', via: [:get, :post], param: 'username'
This is the right answer.
Basically, my username parameter (firstname.lastname) was not being passed as a full string. It is was being passed as firstname instead of firstname.lastname, with the Rails application considering "." to be where the format parameter started ('lastname' was considered a format input in the passed in parameters). I saw these passed in parameters appear in my browser ironically only when I got another error trying something new (basically my application was not responding to 'respond_to |format|' in the 'showobjectdata' method when I tried it pretty randomly - this of course led to these parameters showing up at the bottom of the screen and the googling of a solution. Yes after getting this insight on the parameters, I skipped the respond_to way and once again just rendered the json user object directly as before, without differentiating between the HTML and JSON formats).
So, basically this is the right route that worked for me:
match 'users/showobjectdata/:username', to: 'users#showobjectdata', via: [:get, :post], :constraints => { :username => /[^\/]+/ }
The controller method as originally posted is fine!
Source for the ":constraints =>" part:
Why do routes with a dot in a parameter fail to match?

Check for the existence of multiple params

I'm creating an entry form that I want to only be accessible when three url params are in place: example.com/entries/new/2011/01/27 If someone tries to access any other url (i.e. example.com/entries/new or example.com/entries/new/2011/) I want Rails to set an :alert and bounce the user back to the index page.
Currently, I only have this code in my routes.rb match '/entries/new/:year/:month/:day' => 'entries#new'. What do I need to do to control the redirection if the proper params aren't in the URL? Would I check for each param in the controller and then perform a redirect_to, or is this something I can do from the routes.rb file exclusively? If it's the former, is there an easier way to check that all three params exist other than:
if params[:year].nil && params[:month].nil && params[:day].nil redirect_to ...
This route requires the presence of all three parameters:
match '/entries/new/:year/:month/:day' => 'entries#new'
With only that route, GET /entries/new will result in:
No route matches "/entries/new"
You can redirect from within routes.rb like this:
match '/entries' => 'entries#index'
match '/entries/new/:year/:month/:day' => 'entries#new'
match "/entries/new/(*other)" => redirect('/entries')
The second line matches paths where all three parameters are present. The third line matches all other cases of /entries/new using "route globbing", and does the redirect. Requests matched by the third line will not hit EntriesController#new.
Note: you may not need the first line if you've already defined a route to EntriesController#index -- but watch out for resources :entries, which will redefine index and new.
More info can be found in the guide Rails Routing From the Outside In. When using date parameters, constraints are a good idea (Section 4.2)

Identify GET and POST parameters in Ruby on Rails

What is the simplest way to identify and separate GET and POST parameters from a controller in Ruby on Rails, which will be equivalent to $_GET and $_POST variables in PHP?
You can use the request.get? and request.post? methods to distinguish between HTTP Gets and Posts.
See http://api.rubyonrails.org/classes/ActionDispatch/Request.html
I don't know of any convenience methods in Rails for this, but you can access the querystring directly to parse out parameters that are set there. Something like the following:
request.query_string.split(/&/).inject({}) do |hash, setting|
key, val = setting.split(/=/)
hash[key.to_sym] = val
hash
end
You can do it using:
request.POST
and
request.GET
There are three very-lightly-documented hash accessors on the request object for this:
request.query_parameters - sent as part of the query string, i.e. after a ?
request.path_parameters - decoded from the URL via routing, i.e. controller, action, id
request.request_parameters - All params, including above as well as any sent as part of the POST body
You can use Hash#reject to get to the POST-only params as needed.
Source: http://guides.rubyonrails.org/v2.3.8/action_controller_overview.html section 9.1.1
I looked in an old Rails 1.2.6 app and these accessors existed back then as well.
There is a difference between GET and POST params. A POST HTTP request can still have GET params.
GET parameters are URL query parameters.
POST parameters are parameters in the body of the HTTP request.
you can access these separately from the request.GET and request.POST hashes.
request.get? will return boolean true if it is GET method,
request.post? will return boolean true if it is POST method,
If you want to check the type of request in order to prevent doing anything when the wrong method is used, be aware that you can also specify it in your routes.rb file:
map.connect '/posts/:post_id', :controller => 'posts', :action => 'update', :conditions => {:method => :post}
or
map.resources :posts, :conditions => {:method => :post}
Your PostsController's update method will now only be called when you effectively had a post. Check out the doc for resources.
I think what you want to do isn't very "Rails", if you know what I mean. Your GET requests should be idempotent - you should be able to issue the same GET request many times and get the same result each time.
You don't need to know that level of detail in the controller. Your routes and forms will cause appropriate items to be added to the params hash. Then in the controller you just access say params[:foo] to get the foo parameter and do whatever you need to with it.
The mapping between GET and POST (and PUT and DELETE) and controller actions is set up in config/routes.rb in most modern Rails code.
I think what Jesse Reiss is talking about is a situation where in your routes.rb file you have
post 'ctrllr/:a/:b' => 'ctrllr#an_action'
and you POST to "/ctrllr/foo/bar?a=not_foo" POST values {'a' => 'still_not_foo'}, you will have three different values of 'a': 'foo', 'not_foo', and 'still_not_foo'
'params' in the controller will have 'a' set to 'foo'. To find 'a' set to 'not_foo' and 'still_not_foo', you need to examine request.GET and request.POST
I wrote a gem which distinguishes between these different key=>value pairs at https://github.com/pdxrod/routesfordummies.
if request.query_parameters().to_a.empty?

Resources