Refactoring the Create Action in a Rails App
In this follow up guide we will walk through how to refactor the create action in our Ruby on Rails application to conform to Rails best practices.
Guide Tasks
  • Read Tutorial

In the last guide we built the initial feature for integrating the ability to create records and got all of our request tests passing. In following with the TDD design pattern of Red, Green, Refactor it's not time to refactor the implementation.

Refactoring Strong Parameters

Right now our notification controller code looks like this:

# app/controllers/notifications_controller.rb

class NotificationsController < ApplicationController
  def create
    @notification = Notification.create(params.permit(:phone, :body, :source_app))

    respond_to do |format|
      format.json { render action: 'show', status: :created, location: @notification}   
    end
  end

  def show
  end
end

This code got our tests passing, however we're hard coding the strong parameters into the create method call, we'll need to refactor this if we're going to use strong params in any other methods. Let's create a private method called notification_params and then pass that in when we're creating a new notification:

# app/controllers/notifications_controller.rb

class NotificationsController < ApplicationController
  def create
    @notification = Notification.create(notification_params)

    respond_to do |format|
      format.json { render action: 'show', status: :created, location: @notification}   
    end
  end

  def show
  end

  private

    def notification_params
      params.permit(:phone, :body, :source_app)
    end
end

Now if you run the tests you'll see that everything is still passing and now we have the ability to call the parameters from other methods, such as an update method if we ever need to create that.

Verifying Request Success

Right now our create action is immediately calling the ActiveRecord create method. This is a problem because we don't provide any fallback plan if the action fails and any good API informs the requestor if an error occurs. So we should be first instantiating a new instance of the Notification class and then using the save method in case there is an issue saving the record to the database.

Let's update this in the code:

# app/controllers/notifications_controller.rb

class NotificationsController < ApplicationController
  def create
    @notification = Notification.new(notification_params)

    respond_to do |format|
      if @notification.save
        format.json { render action: 'show', status: :created, location: @notification}
      else
        format.json { render json: @notification.errors, status: :unprocessable_entity }
      end
    end
  end

In this code we're simply instantiating a new Notification and passing it the parameters from the API call instead of trying to immediately add the record to the database. After that, inside our respond_to block we have a conditional that checks to see if the save method works and it responds with the appropriate JSON response.

Now if you run rspec you'll see that we have a failure:

large

And this failure is the exact reason why we needed to perform this refactor! The save call is actually failing and now that our create action is checking for success it's letting us know that there is a problem. The issue is that our strong parameters are missing a require statement, let's fix that:

# app/controllers/notifications_controller.rb

  private

    def notification_params
      params.require(:notification).permit(:phone, :body, :source_app)
    end

This require call is required by Rails, however it was failing silently before we added the conditional, so that's a sign that we're headed in the right direction. Now if you run the tests you'll see that everything is passing, nice work!

large

In the next lesson we'll create a request spec that ensures that clients connecting to the application will be informed when an API doesn't go through successfully.

Resources