1

So in my rails app, I have users with different permissions. When someone is logged in, everything works perfectly. However, when no one is logged in, I get the following error:

Undefined method 'editor?' for nil:NilClass

def require_editor
    redirect_to '/error' unless current_user.editor? || current_user.admin?
end

I think I understand exactly why this is happening: because no one is logged in, Rails is trying to figure out what role they have, and because there is no role for someone not logged it, Rails doesn't know if it should show the page or not.

My question is: How can I make a default role for someone who is not logged in, and send them to the error page that says they don't have permission?

2
  • 1
    Actually, current_user is just nil and nil doesn't respond to editor? or admin?. nil is Ruby's way to describe "nothing". But "someone not logged it" is not nothing. That nothing is something (excellent talk BTW). Using an object in place of missing one is known as the Null Object pattern and it's an elegant way to approach this problem. Where does current_user come from? Are you using devise? Commented Sep 6, 2017 at 14:37
  • 1
    @Stefan: hah, I knew "nothing is something" is something Sandi Metz would say. And, of course, it's her :) Commented Sep 6, 2017 at 14:39

3 Answers 3

5

I usually do something like this:

class ApplicationController

  def current_user_or_guest
    current_user || Guest.new
  end

  class Guest
    def editor?
      false
    end

    def admin?
      false
    end
  end
end

Then simply

def require_editor
  redirect_to '/error' unless current_user_or_guest.editor? || current_user_or_guest.admin?
end

This could be simplified by using CanCanCan and extracting authorization logic from the controller. With cancan abilities set up, controller code usually looks like this:

def require_editor
  redirect_to '/error' unless can?(:edit, MyThing)
end
Sign up to request clarification or add additional context in comments.

1 Comment

This! Extracting authorization logic is the best route to go. It's too easy to start with some conditionals only to become a bigger mess later.
2
def require_editor
    redirect_to '/error' unless current_user && (current_user.editor? || current_user.admin?)
end

You're currently calling editor? on a nil object, which is throwing an error. By using the technique above, you're first checking to ensure current_user is not nil, and then perform the checks on current_user if it exists, if not it simply returns false and redirects to the /error URL.

1 Comment

This is called short-circuiting, it's an important programming concept: When evaluating an and: it stops at the first false. Example: false && raise "Error" won't raise the error. When evaluating an or it stops after the first true.
0

Inside your ApplicationController create a method that redirects to the error page then add a before_action only on the actions that you wanna make sure the user is signed in

ApplicationController

def require_editor
  unless current_user && (current_user.editor? || current_user.admin?)
    redirect_to '/error' 
  end
end

Controller you wanna raise this error

class Controller < ApplicationController
  before_action :require_editor
  def new

  end
end 

If you are using devise gem there is a callback/filter for requiring authentications

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.