Why doesn't direct path for delete/destroy exist in rails? - ruby-on-rails

When you define a link_to in rails to delete an object/item you must specify method delete in that link_to, as compared to edit (edit_event_path(#event)) or show (event_path). Why is this the case?

In typical link_to links the browser will send HTTP GET requests. When you're destroying a resource the browser should send a HTTP DELETE request. Rails has some javascript that will run on those links and intercept the click to send a HTTP DELETE request for those marked with method: :delete. Also the path for a single resource to be destroyed and shown will be the same.
event_path will return "/event/1" or similar. When sending a HTTP GET request its expected that the show action of your controller will be called. When sending a HTTP DELETE request to the same path its expected that the destroy action will be called.

HTTP Verbs
Simply, Rails makes use of the HTTP Verbs which governs the web
Essentially, to keep routing structures simple, Rails allows you to generate a series of the "same" URL paths, each defined with different http verbs:
This means if you want to destroy an object, you can use the delete verb
--
OOP
A more specific definition for this lies with the object-orientated structure of Ruby (& Rails). The routing system is based around this structure (hence why they're called resources) - meaning if you treat the routing system as based around objects, you can begin to see a pattern emerge
If you're going to call a route for an object, IE to destroy that object, your route should be for the "object", not the "destroy" mechanism
In this sense, if you want to destroy an object, it makes much more sense to use the following:
<%= link_to "Destroy", object_path(object), method: :delete %>
This gives you the flexibility to create actions around objects, which can then be routed to the particular controller#actions as required

Related

AJAX + Rails: Should I Use a GET or a PUT When Creating a Route?

My question: When using AJAX within Rails, from a best practice standpoint, does the HTML verb that I use (e.g., get) to define a route matter? Is it OK to basically always use get?
Context: In my Rails App, I've built the following:
When a user selects an HTML checkbox, that triggers a JQuery listener that lives in a file inside of /app/assets/javascripts.
This .js file makes an AJAX call to a controller/action: foos#bar. My JS passes an ID into the action.
To route this request, I've added the following to routes.rb: get "/foos/bar" => "foos#bar". This tells my App which controller/action should process the AJAX call.
The controller/action handles this request just fine. The action grabs the ID as a URL parameter, updates the relevant model object, and finally returns back a 200 to the JS AJAX caller.
This all works great! No issues -- nothing to troubleshoot here.
My question is: In the example above, is it appropriate to define a get route within routes.rb to process this AJAX request? Or, is that bad practice? Should I instead be defining a put, since conceptually that is what I'm doing in this workflow (i.e., I'm updating an existing object)? I'm worried that, while this all works perfectly, I'm breaking some fundamental MVC routing standards.
If you are updating a resource it will most likely be a PATCH update, which means you aren't completely replacing the resource but are just updating it (this is why PATCH is the default HTTP method for updates in Rails, instead of PUT).
So, yes, you are violating RESTful conventions by using GET to update a resource. GET is only used to fetch a representation of a resource...without changing it in any way.

how to give names to rails generated routes?

when using the following on routes.rb: resource :my_model
I get a few automatically generated routes. Some of them have a name (just like when manually defining them with 'as' keyword) but some of them don't.. so how can I give them names?
Or maybe it's a 'hint' given to me by rails that I'm not supposed to use these routes?
What do you refer to when you say "name", the Prefix when you run rake routes? Many of the HTTP requests (i.e. patch, put, delete) are handled by the controllers and are intended to then either redirect to another path or alter the DOM of the current page if you're using javascript, so they wouldn't have a prefix associated with them as those requests don't have an associated view.
When using a singular resource, all CRUD routes will be generated with the exception of an index route. You do not need names for these paths if you are looking to use them as intended. What is your intent for using these routes?
As per the docs:
A singular resourceful route generates these helpers:
new_resourceName_path returns /re/new
edit_geocoder_path returns /geocoder/edit
geocoder_path returns /geocoder
Please post your output via: bundle exec rake routes
You'll notice the show and create controller actions share the same path with the only difference being one expects a POST request and the other a GET request. This is some of the magic provided by Rails allowing you to use similarly named routes that map to different actions.
Try resources instead of resource. If you want a helper for PATCH, PUT or DELETE, just use the helper for the show action (that you'll get from resources) and specify the HTTP method with method:.
The second answer here has a decent explanation about your resource call.
These routes expect a different request method (this implies additional data in request), and do not need separate name, just use generic global_preferences_path and for example - set method for unobtrusive js in link:
<%= link_to 'Set preference', global_preferences_path(some_parameter:'foo'),
confirm:'Are you sure?', method: :put %>
or use html form or so

Is it safe to accept URL parameters for populating the `url_for` method?

I am using Ruby on Rails 4.1.1 and I am thinking to accept parameters (through URL query strings) that are passed directly to the url_for method, this way:
# URL in the browser
http://www.myapp.com?redirect_to[controller]=users&redirect_to[action]=show&redirect_to[id]=1
# Controller
...
redirect_to url_for(params[:redirect_to].merge(:only_path => true))
Adopting the above approach users can be redirected after performing an action. However, I think people can enter arbitraryparams that can lead to security issues...
Is it safe to accept URL parameters for populating the url_for method? What are pitfalls? What can happen in the worst case?
By logging params during requests to my application I noted Rails adds always :controller and action parameters. Maybe that confirms url_for can be used the above way since it is protected internally and works as-like Rails is intended to.
This it is safe internally as Ruby On Rails will only be issuing a HTTP redirect response.
As you are using only_path this will protect you from an Open redirect vulnerability. This is where an email is sent by an attacker containing a link in the following format (say your site is example.com).
https://example.com?foo=bar&bar=foo&redirect=http://evil.com
As the user checks the URL and sees it is on the example.com domain they beleive it is safe so click the link. However, if there's an open redirect then the user ends up on evil.com which could ask for their example.com password without the user noticing.
Redirecting to a relative path only on your site fixes any vulnerability.
In your case you are giving users control of your controller, action and parameters. As long as your GET methods are safe (i.e. no side-effects), an attacker could not use this by creating a crafted link that the user opens.
In summary, from the information provided I don't see any risk from phishing URLs to your application.
Rails redirect_to sets the HTTP status code to 302 Found which tells the browser to GET the new path as you defined it by url_for. GET is a considered a safe method in contrast to
... methods such as POST, PUT, DELETE and PATCH [which] are intended for
actions that may cause side effects either on the server, or external
side effects ...
The only problem would have been if someone could gain access to methods such as create and destroy. Since these methods use HTTP methods other than GET (respectively POST and DELETE) it should be no problem.
Another danger here is if you go beyond CRUD methods of REST and have a custom method which responses to GET and changes the database state:
routes.rb
resources something do
member do
get :my_action
end
end
SomethingController
def my_action
# delte some records
end
For future ref:
Rails has a number of security measurements which may also interest you.
It's not exactly an answer, just wanted to point out that you shouldn't use something like
url_for(params)
because one could pass host and port as params and thus the url could lead to another site and it can get worse if it gets cached or something.
Don't know if it threatens anything, but hey, it's worth pointing out

Rails: Routes, Controllers, Views, Oh My(exclamation)

I'm failing to understand the correlation between routes, controllers, and views (and how they connect to each other).
So, I've got my controller with the index,show,new,create,destroy methods. And the corresponding
GET /entries(.:format) entries#index
POST /entries(.:format) entries#create
GET /entries/new(.:format) entries#new
GET /entries/:id/edit(.:format) entries#edit
GET /entries/:id(.:format) entries#show
PUT /entries/:id(.:format) entries#update
DELETE /entries/:id(.:format) entries#destroy
How come if I add a new method vote_up or vote_down, for example, and the views with matching action names, it doesn't work.
1) What is the proper way to add new actions and connect them to views?
2) Bonus What is the proper way to make these methods ajax-compatible (render a partial with ajax)? What happens if the user doesn't have js enabled?
I may expand/evolve this question based on the answers I get.
I'm tired of googling things like custom action route rails and the like to hodge-podge my apps. It's draining and poor form and I'm finally getting to the level to comprehend the lingo--I've been self taught 100%...so please try to be understanding if you can for a young padawan.
Here's how to think of it, from the beginning:
1) The one and only thing your app does is respond to HTTP REQUESTS.
The most typical kinds of requests are:
GET - the user types something into the URL bar of their browser and hits enter.
POST - the user submits a form.
There are also other kinds of HTTP requests, most importantly PUT, PATCH and DELETE. Rails follows the REST pattern, which means it assigns specific meanings to these HTTP verbs.
2) When any request comes into your app, it has to be routed to a Controller Action.
Your routes.rb file is a set of instructions for the Rails Router (ActionDispatch) that tells the router where to send requests. The "standard" rails resource is given as a shortcut, like this:
resources :things
This means the following:
GET /things => things#index
GET /things/:id => things#show
GET /things/new => things#new
GET /things/edit/:id => things#edit
POST /things => things#create
PUT /things/:id => things#update
DELETE /things/:id => things#destroy
These are considered the standard RESTful actions - nothing else is set by your resources :things declaration. So, if you want the controller to perform other non-standard actions, you have to add them manually.
If you want to perform an action on a specific record, the best way is to use:
resources :things do
member do
get 'vote_up'
end
end
This tells the router that if someone makes a GET request to /things/123/vote_up that it should trigger the ThingsController vote_up action.
All of this is spelled out in great detail in the Rails Guide, you should read the whole thing.
3) Your controller's job is to send a response to the request.
Normally this means something like loading a record from the database and rendering the view for that record.
Each controller action ends by sending the response back to the incoming request. This response can be either a render call - which means send back some data in some format - or a redirect call - which basically makes a new request for you and therefore you get the response of that other request.
In Rails a redirect is effectively sending the request to a different controller action.
A Render call sends data as a response to the request.
When you call render :new, this is a shortcut to render :template => :new, which loads the app/views/things/new.html.erb (or whatever) template, sends it the data from the controller (normally your instance variables) and evaluates this using the template language (erb, haml, etc.) This results in a big string of HTML, which the controller then delivers to the browser.
Want to see what this for yourself? Try ending a controller with render :text => 'Hello World', or even:
render :inline => '<!DOCTYPE html><head><title>Inline Wow!</title></head><body>Mind blown.</body></html>'
See what happens.
When responding (rendering) you can send "normal" HTML templates, with a whole page worth of information in it (head, body, etc.), or a partial that is used by Ajax. You can also send raw data such as JSON or XML. It's all actually just text, and depending on the content of that text (and the HTTP headers that come with it) the browser, script, or client application handles it accordingly.
Again, see the Rails Guide.
4) When the request is made by a browser you probably want to send back HTML. If the request is made by Ajax you probably want to send back JSON.
In the case of a custom action like vote_up you might not want to show a template at all, but just redirect. So, you might have something like this:
ThingsController < ApplicationController
def vote_up
#thing = Thing.find(params[:id])
#thing.vote_up
redirect_to #thing
end
end
Now, one of the benefits of the router is it will give you URL helpers. If you've created the route and action as shown before, on your "show thing" page you could have a URL like this:
link_to 'Vote up this thing!', vote_up_thing_path(#thing)
That would create a link to things/123/vote_up, and if someone clicked on it it would run the code in the vote_up action on the ThingsController, and then redirect back to the show thing view.
5) Your templates send messages to the controllers using links and forms. Links make GET requests, forms make POST requests.
If you want to start having AJAX requests, that's fine. In that case, you just need to make the request in Javascript, and handle the response. So, for instance, you could put something like this in your template:
= link_to 'Vote up this thing', vote_up_thing_path(#thing), :id => 'vote-up-button'
Then in Javascript (with jQuery) you could have a function like this:
$(function(){
$('a#vote-up-button').click( function(event){
event.preventDefault();
$.ajax({
url: this.attr('href'),
type: 'GET',
success: function(){...},
error: function(){...}
});
});
});
In this case the jQuery Ajax method is just making a get request, and then running a callback function based on the response it got.
6) The structure of your controller/routes does not affect what kind of requests you can make, only what action will respond to what HTTP method on what URL.
What you do INSIDE your controller action determines whether you are ready to respond to javascript or html requests etc.
While rails is certainly able to handle multiple request formats in a single controller action, using the respond_to block, as a matter of practicality I find things work much more smoothly when you choose to have routes only respond to one format or another.
IE: I would make your normal page load requests (index, show, new, edit) just HTML requests, and then I would make any additional AJAX actions you want to add be Javascript only -- ie. they respond with JSON instead of HTML. You don't have to do this, of course, but your life will be easier if you do.
I hope this gives you a clearer sense of what is happening in your app. Welcome to Rails, you're joining a great community!
Welcome to the ROR world. :)
Routes, controllers, and views work together to translate a HTTP request into a response of some kind (be it HTML, JSON, or XML). Each attacks a different piece of the problem.
Starting from the end, views are the templates in the rails world and they typically are ERB. ERB is just one templating system, others can be used as well, like haml. Their job is to take some data given to them by the controller and produce formatted output, again typically HTML, JSON, or XML.
But how do you know which view to render for a particular request? How do you get data into your view so that it can do all the fancy dynamic stuff you need? This is where controllers come in. Controllers are ruby classes with the job of examining the parsed HTTP request and any related parameters, fetching data from the database (or wherever), and then passing that data to a view. A controller will typically have several different methods, each corresponding to a different task (e.g. create, show, update, etc).
Lastly, Routes are a DSL for parsing HTTP requests and dispatching a parsed HTTP request to a particular controller method. They are the glue that Rails uses to route URLs to methods, hence the name, routes.
Regarding your specific questions:
1) To create a new action, you have to add a route, a controller method, and a view. So for instance if you wanted to get a count of your entries, you could add a route like:
get '/entries/count' => "entries#count"
This tells ROR to call the count method in the entries controller when that URL is received. Your controller method in this case would be something simple like:
def count
#cnt = Entries.count
end
Then last, you'd make a view in app/views/entries/count.html.erb that had something like:
<p>Count: <%= #cnt %></p>
2) Ajax compatible code really just a way of asking "what if the requested format for this request is JSON?". For this, you'll want to use respond_to method. In a way, respond_to is a formal way of specifying a different view to handle the formatting of the request. To continue with the example above, you could do:
def count
#cnt = Entries.count
respond_to do |fmt|
fmt.html # This just renders the default template
fmt.json { render json: { count: #cnt } }
end
end
N.b. - All code above freehanded. :)
If you have the RESTful controller:
resources :entries do
collection do
get :vote_down
get :vote_up
end
end
this will generate 2 additional routes:
GET /entries/:id/vote_up(.:format) entries#vote_up
GET /entries/:id/vote_down(.:format) entries#vote_down
You can easily change HTTP method to GET, POST, PUT and DELETE.
Also, check "Adding More RESTful Actions" in the routes documentation.

what are the sequence of events that happen when you get a page

Im trying to get my head around the sequence of events that happen between when rails receives a get/post command and the page is served. I was just trying to map out the sequence myself and i realised i dont fully understand myself which scripts is even ran first so I'd like to clear it up in my head.
many thanks
jon
The request goes into the route matcher which parses the URL, parses the config/routes.rb and if the URL matches a route, it looks for a controller file whose name matches the controller part of the URL, (e.g. http://localhost/categories will look for a CategoriesController)
Then, one of two things happen:
If you're using a Rails restful route, the route matcher applies heuristics to figure out which of the 7 actions to call: a GET on a plural last part is mapped to index; a GET mapped to an ID-looking part after the plural is mapped to show (e.g. categories/1 or categories/something or categories/1-something); a POST to a plural last part is mapped to create; a PUT to an ID-looking part after the plural is mapped to update; a DELETE to the same URL is mapped to destroy; new and edit are mapped to GETs for categories/new & categories/edit.
If you have a custom action, you must have a method in your controller object of the same name.
The chosen action is executed and then Rails either renders the template/file/action specified in the render call within the action or it looks for a view file of the same name as the action and that ends with .html.erb (by default) in the app/views/ directory.
Simple.
Rails does quite a lot of things, a good way to get a decent overview is to read the "Action Controller Overview" document at rails guides: http://guides.rubyonrails.org/action_controller_overview.html
The basic structure is:
rack middleware
routing
filters
controller code
rendering
filters
But rails also does many things by itself to the request. It automatically determines what kind of response you want based on your accept headers, and/or if you manually specify which type of response you want with a file ending like /blog/1.xml. Rails also magically creates a nicely formatted params hash, parsing params like user[name]=foo to {:user => {:name => "foo"}}. Rails also has built-in exception handling and some nice stuff to prevent cross site request forgery and much more, so check out the Controller Overview for the lowdown on that.

Resources