1

Conventional Rails best practices seek to reduce the logic code in controllers as those are engineered to route and not perform complex tasks.

However, if you have semi-complex authentication logic, how can you reasonably extract that logic out of the controller?

The following seems to me to be fairly "standard" logic for any basic application. While the logic relates directly to "routing", it seems like I'm putting logic into the controller and it isn't very small...am I going overkill here?

Is it even possible to easily extract this logic into a separate class since the redirect_to ... method is only accessible in the controllers?

class SessionsController < ApplicationController
  # Login page POSTs here to perform authentication logic
  def create
    user = User.find_by(email: params[:email])
    if user and user.authenticate(params[:password]) # default has_secure_password
      if user.confirmed?
        if user.account.active?
          flash[:notice] = "Successfully logged in"
          redirect_to root_path
        else
          flash[:error] = "This account is no longer active"
          redirect_to inactive_account_path(user.account)
        end
      else
        flash[:alert] = "You are not confirmed yet"
        redirect_to confirmation_path(user.confirmation_token)
      end
    else
      flash[:error] = "Invalid email or password"
      redirect_to login_path
    end
  end
end
Dan L
  • 4,319
  • 5
  • 41
  • 74

1 Answers1

1

You can throw all that stuff into callbacks or similar if you like so that the method is a little simpler, but routing should more-or-less belong in the controller.

class SessionsController < ApplicationController
  before_filter :set_user, :check_confirmed

  def create
    if user.account.active?
      flash[:notice] = 'Successfully logged in'
      redirect_to root_path
    else
      flash[:error] = 'This account is no longer active'
      redirect_to inactive_account_path(user.account)
    end
  end

  private

  def set_user
    user = User.find_by(email: params[:email])
    return if user.authenticate(params[:password])
    flash[:error] = 'Invalid email or password'
    redirect_to login_path
  end

  def check_confirmed
    return if user.confirmed?
    flash[:alert] = 'You are not confirmed yet'
    redirect_to confirmation_path(user.confirmation_token)
  end
end

Note that you can put the callbacks in the ApplicationController if you want your SessionsController itself a little leaner.

BUT Remember though that this is the sessions controller, and user state should be managed somewhere else. Ideally, from a logical position, your create method should look something like this:

def create
  user = User.find_by(email: params[:email])
  if user
    flash[:notice] = 'Successfully logged in'
    redirect_to root_path
  else
    flash[:error] = 'Invalid email or password'
    redirect_to login_path
  end
end

And put user status callbacks or similar somewhere else (ApplicationController or whatevs).

t56k
  • 6,769
  • 9
  • 52
  • 115