undefined method `admin?' for nil:NilClass

10,800

Solution 1

current_user will be nil if a user is not logged in according to your code. So you need to do this:

<% if current_user && current_user.admin? %>

or using the try method Rails adds to all objects.

<% if current_user.try(:admin?) %>

Solution 2

as Dogbert said, current_user will be nil if the user is not logged in.

I would suggest two other alternatives:

1) in the current_user method return a special type "guest" user instead of nil. Il will be useful in case you want to do something else with it later, for example in response to some user action.
As inspiration, look at how Ryan Bates explains the Ability class of his gem cancan: link. The first thing he does is creating an unitilized (and not persisted in DB) user. An that Ability class will be instantiated each time Rails will parse an ERB template with that kind of user verification.
So, you could do:

def current_user
  @current_user ||= ((User.find(session[:user_id]) if session[:user_id]) || User.new)
end

So, if (User.find(session[:user_id]) if session[:user_id]) returns nil, the @current_user will be set to an uninitialized User with no identity in DB.

2) define a new metod just to check if the user is an admin, for example:

# your unmodified current_user implementation
def current_user
  @current_user ||= User.find(session[:user_id]) if session[:user_id]
end

def is_an_admin?
  if current_user && current_user.admin?
end

So that you can use it in this way:

<% if is_an_admin? %>
  <div>
    <%= do stuff....%>

...It might be an extra method call, but it might also make your code more readable.

Solution 3

I know this is old, but if someone is googling the error as I did, there is actually no error in Rails Tutorial, but they forgot to highlight one thing they added.

Listing 9.54

before_action :logged_in_user, only: [:index, :edit, :update, :destroy]

Note that they added :destroy action here, not added before, which makes sure that the user is logged to perform destroy action and just then checks if he's an admin

before_action :admin_user,     only: :destroy

Correction:

As of the time of this edit 12/14/2015, the rails tutorial now adds the :destroy action in Listing 9.53. If you miss that one as I did, you will get this error.

Share:
10,800
Holly
Author by

Holly

I am a user who prefers to keep an air of mystery about myself, apparently.

Updated on June 04, 2022

Comments

  • Holly
    Holly about 2 years

    I followed railscast #250 Authentication from Scratch & got everthing wworking fine. Now I'm trying to only display edit & destroy links on my index page to admin user's.

    I've set up mu User database with a admin boolean field & tried putting a simple if statement in the view of another model (hikingtrails) to only display certain links to admin users but I get this error when I try it out, undefined method 'admin?' for nil:NilClass

    Database Schema

      create_table "users", :force => true do |t|
        t.string   "email"
        t.string   "password_digest"
        t.boolean  "admin"
        t.datetime "created_at",      :null => false
        t.datetime "updated_at",      :null => false
      end
    

    User Model

    class User < ActiveRecord::Base
      attr_accessible :email, :password, :password_confirmation#, :admin
    
      validates :email, :uniqueness => true
    
      has_secure_password
    end
    

    Application Controller

    class ApplicationController < ActionController::Base
      protect_from_forgery
    
      # fetch the currently logged-in user record to see if the user is currently logged in
      # putting this method in ApplicationController so that it’s available in all controllers
      private
      def current_user
        # checks for a User based on the session’s user id that was stored when they logged in, and stores result in an instance variable
        @current_user ||= User.find(session[:user_id]) if session[:user_id]
      end
      # to give access to this method from all the views, the helper_method makes it a helper method
      helper_method :current_user
    
      # basic authorization, user must be logged in!
      def authorize
        redirect_to login_url, alert: "You must be logged in to perform this action" if current_user.nil?
      end
    end
    

    views/hikingtrails/index.html.erb

      <% if current_user.admin? %>
        <%= link_to t('.edit', :default => t("helpers.links.edit")),
                  edit_hikingtrail_path(hikingtrail), :class => 'btn btn-mini' %>
        <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
                  hikingtrail_path(hikingtrail),
                  :method => :delete,
                  :data => { :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')) },
                  :class => 'btn btn-mini btn-danger' %>
      <% end %>
    
  • Dogbert
    Dogbert about 11 years
    -1. That won't help this problem at all, and also create a huge security vulnerability.
  • OneChillDude
    OneChillDude about 11 years
    Ya, attr_accessible determines the existence of mass assignment capabilities, not getter methods. You should edit/delete this. Flagged
  • Holly
    Holly about 11 years
    Thanks Dogbert, <% if current_user.try(:admin?) %> worked perfectly for me. I wonder why didn't work <% if current_user.admin? %> as it did before in a tutorial I did in college
  • James OB
    James OB about 11 years
    yes, ok it should be on attr_reader. im just pointing out that its commented out
  • tompave
    tompave about 11 years
    probably because in that tutorial current_user always returned a valid object. See my answer below.
  • dtburgess
    dtburgess over 8 years
    this was exactly correct with respect to the problem I was having with the hartl rails tutorial. However, as of this writing this line is updated in listing 9.53