Lesson Weekend

We've successfully created routes for our record store application. Each route has a corresponding controller action. Now it's time to create our controllers and the necessary CRUD actions that go with this.

Navigate to app/controllers and add the following files: albums_controller.rb and songs_controller.rb. Note that these files must follow a specific naming convention in order to work: <pluralized-class-name>_controller.rb.

Let's start with our albums_controller.rb. We'll create a basic controller that has all seven actions listed. None of the actions will do anything yet, but each has a comment specifying what it will do.

app/controllers/albums_controller.rb
class AlbumsController < ApplicationController

  def index
    # Code for listing all albums goes here.
  end

  def new
    # Code for new album form goes here.
  end

  def create
    # Code for creating a new album goes here.
  end

  def edit
    # Code for edit album form goes here.
  end

  def show
    # Code for showing a single album goes here.
  end

  def update
    # Code for updating an album goes here.
  end

  def destroy
    # Code for deleting an album goes here.
  end

end

Once again, note the naming convention of AlbumsController. This needs to be pluralized and upper camelcase. AlbumsController inherits from ApplicationController which in turn inherits from ActionController::Base. Just as our models folder has an application_record.rb file, our controllers folder has an application_controller.rb file. This file will contain any code that belongs in all controllers.

Next, we have our seven actions in the form of Ruby methods. These methods will always have the same names: index, new, create, edit, show, update, and destroy. If we have custom routes in our application, we'd also have custom methods for those routes. And if we were to use either :only or :except to specify that a group of resources should have fewer than seven routes, that would be reflected in our controller as well.

Much of the code in our controller will look very similar to the code we added to app.rb in the last section. Let's take a look one route at a time.

Index Action


app/controllers/albums_controller.rb
...
  def index
    @albums = Album.all
    render :index
  end
...

Our index action will use Album.all and store the list of albums in @albums so it will be available in the view. This is exactly the same as our code from the last section.

Then we use the render method to return the corresponding view. We could use a custom name but that's not the Rails convention. Our application will automatically look for a view corresponding to albums#index. We'll cover that in the next lesson when we explore views further.

One other thing to note: Rails is so helpful that we can omit the render method altogether if the view is the same name as the controller action and the view is stored in a folder with the same prefix as the controller. This means that if we name our view index.html.erb and store it in the subdirectory views/albums (which we should do), Rails will automatically know that is the correct view for this action. We will continue to add the render method to make our code explicit but this is one more thing that Rails can take care of for us.

New Action


app/controllers/albums_controller.rb
...
  def new
    @album = Album.new
    render :new
  end
...

Our new route first instantiates a new Album and stores it in an instance variable called @album. This is because the form in our view will need access to an instance of Album. This will be clear when we create the view for the form in the next lesson.

Create Action


Here's our new create action:

app/controllers/albums_controller.rb
...
  def create
    @album = Album.new(album_params)
    if @album.save
      redirect_to albums_path
    else
      render :new
    end
  end

  # Other controller methods go here.

  private
    def album_params
      params.require(:album).permit(:name, :genre)
    end

end

The code we need for our create method includes two parts. We'll start by looking at the create method itself. * First, we instantiate a new Album with the album_params from the form. We save this value in the instance variable @album. * Next, we have a conditional if @album.save. Remember that #save returns a boolean while #create returns the created record. * If the album is saved (and the boolean returns true), we'll redirect_to the albums_path. Remember the prefixes from rake routes? We combine the prefix albums with _path. albums corresponds to the index route of our AlbumsController so the code inside def index will run. redirect_to is self-explanatory: it redirects to the path given as an argument. * If the album isn't saved and the boolean returns false, we'll just render :new again.

Now let's take a look at the code under private. Private methods are available only inside the class. That means any methods included under private will only be available inside the AlbumsController class. It's important that we include this code at the end of the class (just above the final end); we wouldn't want to accidentally make any of our controller actions private.

We define a new method called album_params. This is the same album_params we've passed into our create action. This method lets our application know which parameters can be passed into our methods. These are called strong parameters. This is a security feature to protect our database from being attacked. Currently, we only allow two parameters to be passed into an album: name and genre. If someone were to try to pass another parameter in, it would be rejected.

The format of the code in this method will always look like this:

params.require(:<name-of-class>).permit(:<name-of-parameters-to-accept-separated-by-commas)

Make sure that you always add all the properties you want a user to be able to modify to this method. If you are having problems passing parameters into an object, verify that you've added this method along with any necessary parameters. This is a common error for beginners because our application will not throw an exception for missing parameters.

Edit Action


Our edit action will look familiar from the last section:

app/controllers/albums_controller.rb
  def edit
    @album = Album.find(params[:id])
    render :edit
  end

Rails has a params hash just like Sinatra. We use the Album#find method to find an Album instance by the id passed in via params. Then we will render the corresponding edit form for this controller.

Show Action


Our show action looks almost exactly the same as our edit action. The only difference is that we'll render the show view instead.

app/controllers/albums_controller.rb
  def show
    @album = Album.find(params[:id])
    render :show
  end

Update Action


Our update action looks very similar to our create action:

app/controllers/albums_controller.rb
  def update
    @album= Album.find(params[:id])
    if @album.update(album_params)
      redirect_to albums_path
    else
      render :edit
    end
  end

First, we find the instance of Album based on the :id parameter and save it as @album. If @album is successfully updated, our application will redirect_to the albums_path (the index route). Otherwise, it will render the edit form again. Note that the update method relies on album_params just like the save method in the create action does.

Destroy Action


The code for destroying an Album instance is straightforward:

app/controllers/albums_controller.rb
  def destroy
    @album = Album.find(params[:id])
    @album.destroy
    redirect_to albums_path
  end

We find the correct Album instance, use ActiveRecord's built-in destroy method to delete it, and then redirect_to the albums_path.

Each of these actions has functionality that's very similar to what we did in the last section with Sinatra. Either one or all albums are stored in an instance variable so that they can be used in the view. Sometimes these albums are saved or modified. Once the code is finished running, a view is rendered or there is a redirect to another route.

In the next lesson, we'll create the corresponding views for our AlbumsController. We'll build out the SongsController and its corresponding views in a future lesson.

Lesson 10 of 34
Last updated July 14, 2022