Connecting MVC

Exercises

Objective

Now that we have movies stored in the database and a Movie model that gives us access to those movies, it's time to connect the model to the rest of our application. The goal is to end up with a movie listing page that shows the movie information that's in the database. It's a quick exercise that puts everything together to complete the MVC triad, like so:

So let's get right to it...

1. Connect Model and Controller

We'll start by bringing the model into the mix so the controller has less responsibilities.

  1. As a reminder of where things stand, jump back into your browser and go to the movie listing page (http://localhost:3000/movies). You should see three movie titles. Recall that we hard-coded these titles in an array in the index action of the MoviesController, like so:

    def index
      @movies = ["Iron Man", "Superman", "Spider-Man"]
    end
    
  2. Now we want to arrange things so that the array contains Movie objects that reflect the movies in the database.

    Change the index action to fetch all the movies from the database. Assign the resulting array to the @movies instance variable.

    Show Answer

    Hide Answer

    def index
      @movies = Movie.all
    end
    

    Notice there's no need to require the Movie class as you'd need to do in a typical Ruby program. Rails auto-loads files in certain directories, including the models directory.

  3. Then refresh the movie listing page and you should see something like this:

    #<Movie:0x007f9e341d03b0>
    #<Movie:0x007f9e34167130>
    #<Movie:0x007f9e34165dd0>

    That's not quite what we're aiming for, but it's a good start. What we're seeing is the result of calling the default to_s method on a Movie object. Where's that being called? Look back in the index.html.erb view template and you'll see this line:

    <%= movie %>
    

    We're asking ERb to display the value of the movie object. To do that, ERb implicitly calls the object's to_s method which returns that cryptic-looking string. This is progress! It means we're actually dealing with Movie objects now, not just movie title strings.

2. Update the Movie Listing

Now that the @movies array contains actual Movie objects, let's update the movie listing page to show each movie's attributes.

  1. Change the index.html.erb view template to display the values for each movie's title, rating, and total gross. Here's the output you're shooting for:

    Iron Man (PG-13): $585,366,247
    Superman (PG): $300,451,603
    Spider-Man (PG-13): $825,025,036

    Show Answer

    Hide Answer

    <h1>Movies</h1>
    
    <ul>
    <% @movies.each do |movie| %>
      <li>
        <strong><%= movie.title %></strong> (<%= movie.rating %>):
        <%= movie.total_gross %>
      </li>
    <% end %>
    </ul>
    
  2. Now your movie listing should dynamically show what's in the database. That means you can create, update, or delete movies in the database and the movie listing page will change accordingly.

    Give it a whirl! Hop back into a Rails console session and use what you learned in the previous exercise to add, update, or delete movies. Then refresh your browser and you should see the movie listing change accordingly.

  3. Then, just to show off a little, let's format the total gross amount as currency. Formatting numbers as currency is so common that Rails has a built-in view helper method called number_to_currency. If you look at the documentation for this helper method, you'll see that it takes a number and some options. The documentation also includes some helpful examples.

    Go ahead and give it a try! Use the precision option to remove the cents from the total gross value.

    Show Answer

    Hide Answer

    <%= number_to_currency(movie.total_gross, precision: 0) %>
    

    We'll look at helpers in more detail in a future exercise.

Cheat Sheets

We've covered a number of commands and conventions so far, and now might be a good time to download the following cheat sheets for quick reference as you proceed through the rest of the course:

Solution

The full solution for this exercise is in the connecting-mvc directory of the code bundle.

Bonus Round

Vocabulary Check

Learning a new web development framework can feel a bit like learning a foreign language. Here's a bonus exercise to help you practice your new skills.

  1. The ________ connects the requested URL to the appropriate code in the controller.

    Show Answer

    Hide Answer

    This is the job of the router.

  2. Controllers and models share two similar characteristics. They are both Ruby ________ and they both _________ capabilities from a parent class.

    Show Answer

    Hide Answer

    Controllers and models are both Ruby classes and they both inherit capabilities from a parent class.

  3. Are controller names singular or plural?

    Show Answer

    Hide Answer

    They are plural, as in EventsController or MoviesController.

  4. Are model names singular or plural?

    Show Answer

    Hide Answer

    They are singular, as in Movie model.

  5. Are database table names singular or plural?

    Show Answer

    Hide Answer

    They are plural because the table contains many records (or rows) that each represent one event, movie, or so on.

  6. The model gets its power to create, read, update, and delete objects not by magic, but because it __________ from the _________________ class.

    Show Answer

    Hide Answer

    The model inherits from the ApplicationRecord class. ApplicationRecord in turn inherits from ActiveRecord::Base which does all the heavy lifting when it comes to interacting with the database. Subclasses of ApplicationRecord inherit these special database powers.

  7. An _________ is simply a Ruby method defined inside a controller class.

    Show Answer

    Hide Answer

    An action, such as index, is simply a Ruby method. Once again, nothing magical going on here.

  8. A controller action passes data to the view via ___________ ____________.

    Show Answer

    Hide Answer

    A controller action passes data to the view via instance variables, such as @movies.

  9. A typical _____ template is a mixture of ______ tags and _____ tags.

    Show Answer

    Hide Answer

    A typical view template is a mixture of HTML tags and ERb tags.

  10. Which ERb tag runs the Ruby code and substitutes the result back into the template: <%= %> or <% %>?

    Show Answer

    Hide Answer

    <%= %> tags run the Ruby code and substitute in the result. <% %> tags run the Ruby code, but do not generate any output.

Wrap Up

Now you have a model, view, and controller all working in harmony. Each component has its own responsibilities:

  • The controller acts as a middleman. With one hand, it asks the model for application data (movies in this case) and assigns the data to instance variables. With the other hand it tells a view template to display the data in a view. Notice that the controller doesn't tell the model or view how to do their jobs.

  • The model provides convenient access to application-level data. In this case, our Movie model is connected to a database table that contains movie information. The model completely encapsulates the lower-level details of how each row of the database table is translated into a usable object. The model isn't concerned with how the data will be used. Models can also define business logic (behavior) which we'll look at a bit later.

  • The view is simply the presentation of data in an arbitrary format. In this case we used a view template to generate an HTML view. The view doesn't concern itself with how the data it's displaying was assembled.

Here's the thing: It's easy to start adding code to a Rails project and impress your manager with the sheer speed of Rails development. But before you put your hands on the keyboard, it's important to think about where to add the code. Is it a model, view, or controller concern? Because if you start mixing the MVC responsibilities, then you end up with an application that can be very difficult to change down the road. And since change is inevitable, it's best if we're prepared for it. So as we move forward with adding more features, we'll need to be mindful of separating the model, view, and controller concerns.

And on that note, in the next exercise we'll add a few more attributes to our Movie model. That change will force us to think through the parts of MVC that are affected.