MVC: Controllers & Routing

Free Ruby on Rails Tutorial

Delve into this comprehensive Ruby on Rails tutorial to learn more about topics like resourceful and non-resourceful routing, as well as assigning instance variables, and make use of the exercise overview and step-by-step guide to generate a new model for a movie site.

This exercise is excerpted from Noble Desktop’s past web development training materials. Noble Desktop now teaches JavaScript and the MERN Stack in our Full Stack Development Certificate. To learn current skills in web development, check out our coding bootcamps in NYC and live online.

Topics covered in this Ruby on Rails tutorial:

Resourceful vs. non-resourceful routing, Assigning instance variables, What if the names don’t match?, Optional bonus: redirects

Exercise Preview

mvc controllers exercise preview

Photos courtesy of istockphoto, © Korhan Karacan, Image #15095805, Marcello Bortolino, Image #17472015, Sergey Kashkin, Image #318828

Full-Stack Web Development Certificate: Live & Hands-on, In NYC or Online, 0% Financing, 1-on-1 Mentoring, Free Retake, Job Prep. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

Exercise Overview

Earlier, we created a simple controller, but in this exercise we will dive into the heart of the MVC paradigm, learn more about routing requests, and see the difference between Resourceful and Non-Resourceful routing.

  1. If you completed the previous exercises, you can skip the following sidebar. We recommend you finish the previous exercises before starting this one. If you haven’t finished them, do the following sidebar.

    If You Did Not Do the Previous Exercises (3A–3B)

    1. Close any files you may have open.
    2. Open the Finder and navigate to Class Files > yourname-Rails Class
    3. Open Terminal.
    4. Type cd and a single space (do NOT press Return yet).
    5. Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
    6. Run rm -rf flix to delete your copy of the Flix site.
    7. Run git clone https://bitbucket.org/noble-desktop/flix.git to copy the Flix git repository.
    8. Type cd flix to enter the new directory.
    9. Type git checkout 3B to bring the site up to the end of the previous exercise.
    10. Run bundle to install any necessary gems.
    11. Run yarn install --check-files to install JavaScript dependencies.

Getting Started

  1. Open the Finder and navigate to Class Files > yourname-Rails Class

  2. Open Terminal.

  3. Type cd and a single space (do NOT press Return yet).

  4. Drag the flix folder from the Finder to the Terminal window.

  5. Make sure you’re in Terminal and hit Return to change into the new folder.

Generating a New Model

Choosing where to start a new project is generally challenging; when building a Rails app, it generally makes the most sense to start with your data storage, which means creating a model.

The Flix site is going to be all about movies, so our first step will be to create a model of a movie.

The command we’ll use to create the model will look a great deal like the command we typed to generate the recipes model with scaffolding:

rails generate scaffold recipe title:string description:string prep_time:string ingredients:text directions:text

After creating this scaffolding, we could access properties of each recipe as methods of the @recipe instance variable. For example, we added the recipe’s title to the <title> tag of the page by referring to @recipe.title.

  1. Type the following in Terminal, but do not press Return after typing it!

    rails generate model movie
    

    Before we add the components of the movie model, let’s look at the different kinds of fields available in Rails:

    • string: single-line text
    • text: multi-line text
    • integer: 1, 2, 3…
    • decimal: 1.5, 3.14…
    • boolean: true/false (which will create methods ending in ? question marks)
    • date, time, datetime: just like the names suggest, this can be the date, the time, or the combination of the two

    Our movie model will incorporate all of these field types, so you will gain experience using all of them.

  2. Add the following to your partially-finished Terminal command. Press Return after typing this time. (Take a moment to double-check your work first!)

    rails generate model movie title:string description:text placement:string mpaa_rating:string runtime:integer poster:string has_subtitles:boolean ticket_price:decimal release_date:date
    

    Rails will then create several files for us, which are listed next to the green word create. The most important of these is app/models/movie.rb, the model file itself. There are also two test files, but we’re not going to worry about those yet. At the top of the list is a migration file. Its name starts with db/migrate/... and then a unique string of numbers. Let’s take a closer look at this file.

  3. In the Finder, navigate to flix > db > migrate > #_create_movies.rb

    NOTE: The # stands for the unique numerical filename, which is the timestamp of its creation and will be different for everyone.

    Rails migration filenames always start with a timestamp so Rails can run them in the proper order. This is why it’s important to use the rails generate command from the Terminal and never create model files by hand.

  4. Open #_create_movies.rb in your code editor. Rails created this file from the information in that long rails generate model movie command we just entered; let’s see what Rails put in this file automatically.

  5. Look at the first lines of the document:

    class CreateMovies < ActiveRecord::Migration[6.1]
       def change
    

    This means that Rails has created an object of the CreateMovies class and it inherits from ActiveRecord::Migration. (The brackets indicate the version of Rails that created this migration; this is for backward compatibility.) Inside is a single method called change, which indicates that the migration within is reversible. (Migrations need not be permanent; they can be applied and rolled back as needed during the development process.)

  6. On the next line, within the change method is a call to create_table:

    create_table :movies do |t|
    

    This code creates the structure of the movies database.

Editing a Migration File

Although it’s convenient to create columns from the command line with rails generate, it is perfectly appropriate to add, remove, or edit columns here before applying the migration. Let’s try making a change to the file now.

  1. Find the following piece of code, around line 9:

    t.string :poster
    
  2. Make the following change shown in bold:

    t.string :poster_image
    

    Let’s also add some defaults for the ticket price field. It’s a good idea to do this with decimal values so they don’t display hundreds of digits after the decimal point and the numbers can be shared between different databases.

  3. Find the following piece of code, around line 11:

    t.decimal :ticket_price
    
  4. Add the following bold code:

    t.decimal :ticket_price, precision: 8, scale: 2
    

    Precision indicates the overall size of a field. Scale indicates how many places should be after the decimal point. With these precision/scale settings, the ticket price will have a maximum of 8 total digits (6 digits before the decimal point and 2 after) which should allow Flix plenty of room to grow!

  5. Notice the following piece of code around line 14:

    t.timestamps
    

    We didn’t create this column; Rails generated it automatically. The timestamps column in a Rails database keeps track of every migration’s timestamp, ensuring the database functions properly.

  6. Save the file.

  7. Switch to the Terminal.

  8. Type the following in Terminal:

    rails db:migrate
    

    This applies the migration. The changes we made in the migration file have now been committed to the database.

Populating a Database with a Seed File

At this point, you’re probably wondering: How do we add data to the database? Well, there are several ways, but at this moment we’re just concerned with getting a few movie records (with all the required information, like runtime and title) entered in the database for development purposes. Rails provides a handy way for developers to quickly populate a database with data for testing purposes with something called a seed file.

We have created a seed file in advance to save you some time. Let’s copy it to the correct directory and check it out.

  1. Open Finder and arrange two windows side-by-side.

  2. In one window, navigate to the destination for the seed file: flix > db

  3. In the other window, find Class Files > yourname-Rails Class > flix snippets > seeds.rb

  4. Copy seeds.rb from flix snippets and paste it into the flix > db folder.

  5. You will see an alert about replacing a file with the same name; click Replace. (We are overwriting the automatically created blank seeds file.)

  6. Open the newly copied seeds.rb in your code editor.

    Take a look at the code that starts on line 8 (after the commented-out part). It starts with a call to Movie.create, then has an array of hashes. You should be able to scan over the code and recognize all the fields that are part of the movies model, interspersed with sample data.

    Seed files are usually created by hand and used whenever you have to move a development site around (or share it between multiple developers). The seed file is pretty simple, and we can apply the file to the database by running a simple command. Let’s try it!

  7. Switch to the Terminal.

  8. Type the following to apply the seeds.rb file to the database:

    rails db:seed
    
  9. That’s it! We now have a database with some sample movie data loaded. You can’t see it yet, but we’ll handle that in the next section.

Non-Resourceful Routing Explained

Let’s review quickly what we did earlier to create a Non-Resourceful Route to get the localhost:3000/movies page to start displaying properly:

  • We generated a new controller using rails generate controller.
  • In the routes.rb file we added a route to /movies.
  • We added the about action to the MoviesController by creating a method called about. It was enough to simply create the empty method; we didn’t have to put anything in it.
  • We created a new view with some really simple HTML, called it about.html.erb, and saved it in the proper folder. Rails automatically knew to load it because of the directory and the name of the method.

Non-Resourceful Routing vs. Resourceful Routing

We have previously tried Non-Resourceful Routing. This routing process is very powerful: you can map any URL to any controller in any action, but it comes at a cost. That cost is complexity. Imagine if we had to create a route for every movie submitted, doing this same process for movies/star-wars and movies/the-godfather… We could be routing movies all day.

Fortunately, there’s a better way. It’s called Resourceful Routing. Resourceful Routing assumes that a controller implements several standard methods associated with the basic CRUD (Create, Read, Update, and Delete) functions. Let’s take a look at the way Resourceful Routing works.

Resourceful Routing

  1. In your code editor open flix > config > routes.rb.

  2. Edit the code as shown in bold:

    Rails.application.routes.draw do
       get 'about' => 'movies#about'
    end
    
  3. Save the file.

  4. Switch to the browser.

  5. Reload localhost:3000/movies and notice that it no longer exists.

  6. Navigate to localhost:3000/about which displays correctly—it is the new home of the about page!

  7. Switch back to routes.rb in your code editor.

  8. Add the following bold code:

    Flix::Application.routes.draw do
       get 'about' => 'movies#about'
       resources :movies
    end
    

    With this single line of code, we have just created a great number of routes, which you’ll see in just a moment.

  9. Save the file.

  10. Back in Terminal, hit Ctrl—C to stop the server.

  11. Type the following in Terminal:

    rails routes
    

    This command will always show you all the available routes in your application. Now that Rails knows that movies are a resource, it can make some assumptions about routing. Here are the most important ones:

    • Rails expects an index (or an overview list of movies) to exist at /movies
    • Rails expects to serve a details page for /movies/:id (which might be something like /movies/1 or /movies/shrek).
    • GET routes are created in order to serve pages: movie data for viewing, for example, or to help create movies or edit existing movies. There are also PUT, PATCH, POST, and DELETE routes which will receive user input and perform the requested actions.

    We’re going to start with the index action, which is the most basic.

Catching the CRUD

Another term for what resourceful routing does is CRUD. CRUD stands for Create, Read, Update, Delete. These four operations describe the lifecycle of an object in Rails. Each of these actions corresponds to an HTTP verb:

  • Create = POST
  • Read = GET
  • Update = PUT/PATCH
  • Delete = DELETE

What’s the difference between PUT and PATCH? PUT is intended for updates that overwrite an entire record, while PATCH is for partial updates—just a single field or two.

Assigning Instance Variables

  1. In your code editor open flix > app > controllers > movies_controller.rb

  2. Add the following bold code to create a new method:

    class MoviesController < ApplicationController
       def about
       end
    
       def index
       end
    end
    
  3. Save the file.

  4. We know from our earlier trial-and-error process that we’re going to need an index view file for this to work. To save you the work of writing the HTML, we’ve provided a file for you­—we just need to copy it into the right place. Switch to the Finder.

  5. Navigate to yourname-Rails Class > flix snippets and copy index.html.erb

  6. Navigate to flix > app > views > movies

  7. Paste it (Cmd–V) into the movies folder.

    We’re going to be writing views of our own soon enough, but this view file is just to get us up and running. So now we have our controller, our action, and our view. Let’s see how our site is looking!

  8. Type the following in Terminal:

    rails server
    
  9. In the browser, attempt to navigate to localhost:3000/movies­ and notice that it displays an error. But it’s an error we’ve never seen before, so it will be a good learning opportunity.

    This is a good time to point out that Rails does a really great job of helping you debug. Rails is even highlighting the exact line in the source code where things are falling apart.

    So why did we get this NoMethodError? Because our view was expecting to display a list of movies using an instance variable called @movies. One of the most important functions of a controller (besides routing) is to assign instance variables. Having received a request from the user, such as “Show me a list of movies!” it is the controller’s duty to fetch that list of movies and share it with the view to render to the user.

  10. This error needs to be fixed inside of our movies controller. Switch to movies_controller.rb in your code editor.

  11. Add the following bold code:

    class MoviesController < ApplicationController
       def about
       end
    
       def index
          @movies = Movie.all
       end
    end
    
  12. Save the file.

  13. Switch to the browser and reload localhost:3000/movies

  14. Notice that our page is now displaying properly, with a list of three movies!

    Let’s look back at the line of code we just wrote. The most important part is the @ sign, which made @movies an instance variable. Remember when we used @name earlier in the Ruby exercises? The @ sign is there so that the variable will be available outside of the index method (for example, to the view file that it’s calling).

  15. In the browser, navigate to localhost:3000 and notice it’s still the default Rails welcome page. Let’s fix that.

  16. Switch to routes.rb in your code editor.

  17. Add the following bold code:

    Flix::Application.routes.draw do
       get 'about' => 'movies#about'
       resources :movies
       root 'movies#index'
    end
    
  18. Save the file.

  19. Switch to the browser, reload localhost:3000 and notice that the default You're riding Ruby on Rails page is no longer there—it’s been replaced by our movies index page. Our Flix app is really coming along!

Optional Bonus: Redirects

Redirects are another one of those necessary evils on the Internet. Suppose we put together a newspaper advertisement to promote Flix, and that ad accidentally directed people to flix.com/films instead of the real address, flix.com/movies. Rather than firing our copy editor, we could easily create a redirect in routes.rb. Let’s try doing that now.

  1. Switch back to routes.rb in your code editor.

  2. Add the following bold code, taking a second to double-check the position of the various punctuation marks:

    Flix::Application.routes.draw do
       get 'about' => 'movies#about'
       get '/films', to: redirect('/movies')
        resources :movies
       root 'movies#index'
    end
    
  3. Save the file.

  4. Switch to Terminal and type the following to start the server:

rails server
  
  1. Switch to the browser and navigate to localhost:3000/films and notice that it redirects swiftly to localhost:3000/movies just like we wanted!

    What if our board of directors had a meeting where they determined that films is a classier and more appropriate way to refer to movies? What if they told us to use flix.com/films as the basis for our site—do we have to rename our model? Do we have to delete all of our data and start over? Do we cry? No! All we have to do is add a bit of information to our resource.

  2. Switch back to routes.rb in your code editor.

  3. Make the following changes shown in bold, taking a moment to double-check that all the commas and colons are in the proper place:

    Flix::Application.routes.draw do
       get 'about' => 'movies#about'
       get '/films', to: redirect('/movies')
       resources :films, as: :movies, controller: : movies
       root 'movies#index'
    end
    
  4. We also need to reverse our redirect, as shown in bold:

       get 'privacy' => 'movies#privacy'
       get '/movies', to: redirect('/films')
       resources :films, as: :movies, controller: :movies
       root 'movies#index'
    end
    
  5. Save the file.

  6. Switch to the browser and navigate to localhost:3000/films and see that it was really that simple to reorganize the entire site!

    What if that board of directors had another meeting and decided to reverse the whole change? Well, their wish is our command.

  7. Switch to routes.rb in your code editor.

  8. Revise the code to read as follows, making sure to delete the entire redirect line:

    get 'about' => 'movies#about'
       resources :movies
       root 'movies#index'
    end
    
  9. Save the file.

  10. Switch to the browser and navigate to localhost:3000/movies

    Now our site is back to its original configuration. Thanks to that finicky board of directors, now you know that if you did want to have a controller and a route with a different name than your model, it’s entirely possible to arrange that. Rails leaves it completely up to you.

  11. Back in Terminal hit Ctrl–C to shut down the server.

Noble Desktop Publishing Team

The Noble Desktop Publishing Team includes writers, editors, instructors, and industry experts who collaborate to publish up-to-date content on today's top skills and software. From career guides to software tutorials to introductory video courses, Noble aims to produce relevant learning resources for people interested in coding, design, data, marketing, and other in-demand professions.

More articles by Noble Desktop Publishing Team

How to Learn Coding

Master coding with hands-on training. Learning how to code in JavaScript, Python, and other popular languages can pave the way to a job in tech, such as web development, data science & analytics, or software engineering.

Yelp Facebook LinkedIn YouTube Twitter Instagram