The very first and foremost component to comprehending the REST route workflow for Rails is understanding the back-and-forth communication between the client, the application server, and the database. It’s all too easy for the young coder (like myself) to become preoccupied with the code and the views without comprehending the routes that are essential for communicating, storing, and showing the information that is being input.
Several actions occur during the execution of a full REST workflow. The client first, for example, fills out a form that the programmer has provided for them in a view. This information (the user info, their content, as well as any additional information) is sent to the application server for interpretation in form of params. The server uses the code the programmer has written to interpret the information and apply it in the method(s) that it has been written to execute and then generate that information into the database. There may be background tasks that the server is written to execute as well, such as updates, notifications, etc. The server then sends a response back to the client (could be an error message, could be a post notification, etc.). This server response, pending no errors, tells the browser which view to display to move the client forward in their interface. The browser receives this information and displays it for the user.
Fortunately for Rails users, this skeleton for coding this process is written into the framework. RESTful conventions execute four key CRUD actions: Create, Read, Update, and Destroy. Included in these CRUD actions, at their most base level, a functional code will provide the program with several executive functions: the ability for the user to create and display (read) a list of whatever the content may be, as well as interfaces (views) for updating and deleting this content in and from the database.
The basic RESTful conventions each come with a Method and an Action. The methods contain our HTTP verbs which direct our routes, while the actions contain our URL actions.
Our standard HTTP verbs are GET, POST, PATCH, and DELETE.
- GET routes us to an erb in the web browser that shows or renders elements from the database or a specific form for interacting with it
- POST creates a new instance in our database by sending data to the server so that the server can apply this data and create a new resource
- PATCH (or PUT) allows us to update the instance in our database, which requires an id to direct which instance is being update
- DELETE allows us to delete the instance, also reliant on a specific id
Generally, the programmer will have the code written with rules that allow only a specific user to PATCH and DELETE and sometimes even view content that they personally POSTed under their user_id (unless that instance is intentionally available for any user to alter within the database).
All of these relationships between controller redirects and erbs is mapped out in the MVC directories in the app directory.
Create and Read Actions
The Create CRUD action does exactly what it sounds like: it instantiates a new unique object containing the available keys for that object. We can use the Rails console to manually create an instance using language that Ruby users are already familiar with. In the example of creating a book for a Book Club (which features keys for :title, :author, :genre, and :description), we can open the Rails console and enter the following commands:
book = Book.new #this creates a new instance of book in our array
book.author = "Haruki Murakami"
book.title = "Kafka on the Shore"
book.genre = "Magical Realism"
book.description = "Boy runs away and learns about himself and his mom and does spooky things in a library and a forest. Also a guy talks to cats."
book.save #saves our new instance into a db
The save method will show us an output describing the instance’s insertion into the database (this output also includes the assumption that the database includes a timestamp key, which is a valuable component for logging create and update actions):
INSERT INTO "books ("author" "title", "genre", "description", "created_at", "updated at") VALUES (?, ?, ?, ?, ?, ?) [["author", "Haruki Murakami"], ["title", "Kafka on the Shore"], ["genre", "Magical Realism"], ["description", "Boy runs away and learns about himself and his mom and does spooky things in a library and a forest. Also a guy talks to cats."], ["created_at", "#timestamp"], ["updated_at", "#timestamp"]]
This is essentially the same function of our create method in the BooksController, though in the controller we write it in a way that accepts any set of unique params, as opposed to the ones we manually input; we write a method for create that resembles the manual entry but with the flexibility of params instead of hard code:
book = Book.new #just like we wrote in the manual entry
book.author = params[:author]
book.title = params [:title]
book.genre = params[:genre]
book.description = params[:description]
book.save #just as above
Now or code is written to accept any unique instantiation created by a user with their own supplied params. While users are now able to create a new book in theory, they are still not able to do so in practice without a create view, which we add in app/views/books/new.html.erb. Now all that’s missing is a redirect to that view written into the create method in the BooksController. We can see that in the server log that the instance was INSERT[ed] INTO “books”, so we add a refactored Rails style redirect (which redirects us to the show page) that says:
@book = Book.new
@book.author = params[:author]
@book.title = params [:title]
@book.genre = params[:genre]
@book.description = params[:description]
Alternatively, this same method can be written as:
@book = Book.create(author: params[:author],
title: params[:title], genre: params[:genre], description:
In order for the user to actually create a book, however, they will need to use the form we created for them on the new.html.erb view that, at its most basic state, will look something like this:
<h2>Create a Book!<h2><%= form_tag books_path do %>
<%= text_field_tag :author %><br> <label>Title:</label><br>
<%= text_field_tag :title %><br> <label>Genre:</label><br>
<%= text_field_tag :genre %><br> <label>Description:</label><br>
<%= text_area_tag :description %><br> <%= submit_tag :'Submit Book' %><% end %>
Now that we have created an instance through the new page and redirected to the show page, we have completed both the Create and Read components of our CRUD action for a specific book instance (or for any amount of instances created by users).
It’s great that we were able to allow the user to create a book in our book library to view, but at this point, this is all they are able to do. Suppose they want to edit the description after they’ve read it, or they misspelled the author’s name, or the book has a different title for the American version than for the English version. Not only can the user not edit or update the information, they can’t even delete the false instance they created. They can only stare at it endlessly and reflect on all of the wrong choices they’ve made in their life and eventually write a letter to the developer blaming them for various irreparable crises they’ve encountered along their woebegone timeline. Since we’re already burdened with regret for our own decision to get that expensive liberal arts degree or two before we finally settled on the coding path, we’re gonna make sure the user’s existential burden stays off our plate by creating a means for them to edit or update their instance.
First things first — we create a get route to access our edit form. Because we are working with a dynamic route (which means that the user’s view and the page URL changes based on the specific content it refers to while reading from the same form and controller), this will include an :id parameter to signify a unique instance per book for our controller to access.
get 'books/:id/edit', to: 'books#edit', as: :edit_book
In order to complete the update action, however, we will need to write a second route based on the same :id that uses our Patch HTTP verb:
patch 'articles/:id', to: 'articles#update'
You can also accomplish this by simply adding the :edit and :update actions to the resources call in the routes file.
Once we have these added, by whatever means, we will want to add the methods for edit and update to our BooksController (at this point we will almost certainly want to write some code in the controller that prevents a user from accessing and Updating or Deleting, or sometimes even Reading, someone else’s content, but that’s neither here nor there at this moment in our most simplistic code framework).
We could start writing our edit form on the edit.html.erb view, but at this point, we haven’t used the controller to give the form access to the record’s data by creating and populating a variable. So let’s do it:
@book = Book.find(params[:id])
Now we can use @book in our edit form (app/views/books/edit.html.erb) to access a specific book based on the specific associated :id. This form will look very similar to the one we wrote for our create page:
<% form_for @book do |f| %> <%= f.label 'Author' %><br>
<%= f.text_field :author %><br> <%= f.label 'Title' %><br>
<%= f.text_field :title %><br> <%= f.label 'Genre' %><br>
<%= f.text_field :genre %><br> <%= f.label 'Description' %><br>
<%= f.text_area :description %><br> <%= f.submit "Submit Book" %><% end %>
The biggest difference between the Update and the Create forms to note is “form_for”, which indicates that we are updating an existing instance of the Book class, not creating a new one. It automatically sends our information down the Update path and populates it with the newly sent params. If we follow this route to our update action in the BooksController, we see that we need to add some code to tell our update action what exactly to do:
def update @book - Book.find(params[:id]) #match the routed :id to the
database and store it in an instance variable @book.update(author: params[:author], title: params[:title],
genre: params[:genre], description: params[:description]) #update
the params given in the form and saves them to the database redirect_to book_path(@book) #redirect to the updated show pageend
We did it! We’ve freed the user from the confines of their own human error, as well as built Create, Read, and Update actions for our Book Club! Now we need to provide them with the option of deleting their content.