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
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
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation#, :admin
validates :email, :uniqueness => true
has_secure_password
end
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
<% 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 %>
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?) %>
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.
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With