Rails is very public in embracing REST-style architecture as a default. Different HTTP verbs can result in different methods being triggered in your controller. In our example today we have a
purchase with an
/purchases/2 will then behave very differently based on the HTTP request.
GET /purchases/2 will trigger the
show action, and display the purchase itself.
PATCH /purchases/2 will trigger the
update action, which is called after submitting a form from the
purchases/2/edit page (you actually can’t generate an HTTP PATCH request from a form because W3C standards say you can only use
action=get from an HTML form, but Rails fakes it via
value="patch" hidden form fields).
DELETE /purchases/2 will trigger the
destroy action, and remove the record.
When you scaffold out the purchase, a nice listing page gets generated. From the
/purchases page you can view, edit, and delete purchases.
When you click on
delete you get a nice little dropdown confirming your choice.
You can click OK to confirm and delete, or Cancel to halt.
The question is, how is this generated? If you take a look at the generated source code you’ll see something interesting.
You can see this is a simple HTML anchor tag. Inside we have an
href attribute with the expected
/purchases/2, the item we’re dealing with. We also have a
rel=nofollow attribute, which is supposed to signal to search engine crawlers not to follow the link (although many do in fact follow the link, but use the
nofollow attribute to determine whether it should index the linked page, or use it in calculating the page rank etc.).
We also have two
data-confirm="Are you sure" and
Rails uses these two attributes to attach event handlers to the elements that together will prompt the user to make sure the click wasn’t accidental, and build a form that can be POSTed to the server (with an appropriate field marking it as a DELETE request) to actually delete the record.
It does so by using
It’s loaded by default in the
application.js manifest file in each Rails app.
Hey that has our
data-method attributes. So what is done with
rails.linkClickSelector? Well, a little further down you’ll find the following:
This uses the jQuery
delegate method. As you can see by the docs our first argument is the “selector”. Basically, we’re going to attach an event listener to all elements that match the selector.
rails.linkClickSelector is a string of a number of different attributes, all of which will be matched. EventType is going to be
click.rails. What this means is that we’re going to capture all
click events, and it’s simply going to be namespaced with
rails. It’s still a regular click event, everything after the
. doesn’t effect the actual event at all, but this way user code can differentiate between say,
click.whateverelse. This way you could unbind
click.rails without affecting other click handlers from other libraries or code. It’s a way of being a nice neighbor.
Finally, the handler is passed in as an anonymous function.
In the handler, first the function saves the current context (
var link = $(this)) and gathers up info such as
method. We get the value of
data-method with the call
link.data("method"), which in this case returns “delete”.
Then the function checks to see if this action is “allowed”. It does this by calling
rails.allowAction looks like this:
allowAction the function pulls the message to be displayed from our
data-confirm attribute via
data-confirm hasn’t been provided the assumption is that the action is allowed, so
true is immediately returned.
confirm event is fired on the element, so you could intercept and cancel the confirmation should you wish to do so.
That doesn’t happen here, so the function calls the
rails.confirm simply accepts our message and passes it to the
window.confirm function. This way you can go with a different route by overriding
allowAction completes, and we head back into our anonymous function. This isn’t a remote call, so we deal with it via
This is where the actual deletion request occurs.
href of our link. (
data-method value. (
Get the target value. (
Get the csrfToken. (
Get the csrfParam. (
Build the form. Plug in the action, the _method, and the CSRF stuff and target if necessary.
Notice we’re not actually putting anything substantive into the form. The simple reason is the request to a particular endpoint (
/purchases/2) with a particular varb (
delete) achives our result. We don’t need other values in the form.
Now we just submit the form in the background:
And we’re done.