I have authlogic running just fine in my app, but I am rolling my own roles (i am newer to rails and wanted to learn...)
So I have a User model, a Role Model, and a User Sessions model. User acts_as_authenticated.
In my application_controller
protect_from_forgery
helper_method :current_user, :is_admin, :is_group_coach, :is_group_leader
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
def is_admin
current_user.role_id == 3
end
def is_group_coach
current_user.role_id == 2
end
def is_group_leader
current_user.role_id == 1
end
Then I am doing a simple if is_admin in a view...
but its returning undefined method `role_id' for nil:NilClass
I think its doing this because current_user is actually running off the UserSession model not User... How can I modify this to run as expected?
Your current_user_session method is probably incomplete on this code snippet as you can't call find without parameters, so I'm guessing there is a guard in there against a nil value or somewhere like that if the user is not logged in. And if there is the possibility for the user to not be logged in, your methods should account for that and only call methods on current_user if one is available.
Your methods should be like this:
def is_admin
current_user && current_user.role_id == 3
end
def is_group_coach
current_user && current_user.role_id == 2
end
def is_group_leader
current_user && current_user.role_id == 1
end
This will prevent the test from breaking is there is no user currently logged in on the website.
Related
I'm using devise and have set up helper methods to check if any type of user is signed in. I have player and coach user types and current_player and current_coach are devise methods that exist.
Here is my application_controller:
helper_method :current_account, :account_signed_in?
def current_account
if #current_player
#current_account = current_player
elsif #current_coach
#current_account = current_coach
end
end
def account_signed_in?
current_account != nil
end
The player and coach models are submodels of the user model.
class Player < User
end
class Coach < User
end
This works (but only if there is a current_player):
def current_account
#current_account = current_player
end
If I remove the # from current_player and current_coach I get an error:
wrong number of arguments (given 10, expected 2)
current_account stays nil because neither your if condition nor your elsif condition are true. It means that #current_player and #current_coach are nil (or false)
I have two model vendor and customer, in the application controller I defined after_sign_in_path_for method for each model, but after sign up and confirm for customer, when I log in, I redirected to vendor log in page instead of dashboard_path. Is this problem occur because I put both model's methods in the application controller, should I have put each method in their appropriate sessions controller?
I believe the cause the the wrong redirection is an endless loop. Is there any wrong with the application controller code below?
I have only written in this application controller, everything else is devise built in.
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :authenticate!
def after_sign_in_path_for(user)
if user && user.is_a?(Customer)
customer_dashboard_path
elsif user && user.is_a?(Vendor)
vendor_dashboard_path
end
end
def after_sign_out_path_for(user)
if user && user.is_a?(Customer)
root_path
elsif user && user.is_a?(Vendor)
root_path
end
end
def after_inactive_sign_up_path_for(user)
if user && user.is_a?(Customer)
root_path
elsif user && user.is_a?(Vendor)
root_path
end
end
def authenticate!
if #current_user == current_customer
:authenticate_customer!
elsif #current_user == current_vendor
:authenticate_vendor!
end
end
end
you are have same method written twice(method override) with different params. So it will always pick the second method after_sign_in_path_for(vendor). You can change the method names to what you are expecting for example and call methods accordingly.
def after_sign_in_path_for_customer(customer)
dashboard_path
end
def after_sign_in_path_for_vendor(vendor)
dashboard_path
end
Or else you can have a common method like following
def after_sign_in_path_for(user)
if user && user.is_a?(Vendor)
redirect to vendor dashbord
elsif user && user.is_a?(Customer)
redirect to customer dash board
end
end
In Michael Hartls rails tutorial , I came across the following code :
module SessionsHelper
def current_user=(user)
#current_user = user
end
def current_user #get logged in user
#current_user||=User.find_by_remember_token(:remember_token)
end
def sign_in(user) #sign the user in by setting cookies
cookies.permanent[:remember_token]= user.remember_token
current_user = user
end
def signed_in?(user) #check whether user signed in
!current_user.nil?
end
end
My SessionsController create action looks like this :
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user # sign the user in by setting cookies
else
flash.now[:error]= "Invalid email/password"
render 'new'
end
end
Why do we need a writer method def current_user=(user) in the SessionsHelper module ?
Can't we just assign the value of user to the #current_user variable directly in the sign_in method ?
Can it be done like this:
module SessionsHelper
# Notice that there is no def current_user=(user) method.
def current_user
#current_user||=User.find_by_remember_token(:remember_token)
end
def sign_in(user)
cookies.permanent[:remember_token]= user.remember_token
#current_user = user #set the current user directly withoout a writer method
end
def signed_in?(user)
!current_user.nil?
end
end
Sure you can do like that. But idea is to incapsulate instance variable and never assign it directly outside getter and setter methods and that's how ruby works. You actually cannot do SomeObject.#instanceVar=. you need setter for that.
So if in future you will need to set current_user eather you have to use sign_in method or create new method which will be like exactly setter. So why not to use it?
please help me to understand something. In Authlogic example in UsersController it's always used #current_user, so for instance:
def show
#user = #current_user
end
(taken from http://github.com/binarylogic/authlogic_example/blob/master/app/controllers/users_controller.rb)
Why is that? In my controllers I use just current_user instead of #current_user.
And besides - Authlogic works perfectly for me, but I don't see magic columns being populated (like last_login_at or last_login_ip). Should I initialize them somehow specifically besides just adding into migration?
UPD
After some investigation, I found that if there're only fields last_login_at and last_login_ip from "Magic fields", then they will not be populated. If I add a full set of magic fields, it is working perfectly.
UPD2
My concern regarding current_user is only about UsersController: why does it have #current_user and not current_user?
current_user is typically a method defined in app/controllers/application_controller.rb which sets the #current_user instance variable if it is not already defined -- here is an example:
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
Re the "magic columns", these should be set by Authlogic automatically. For example, if your user sessions controller logs in a user:
#user_session = UserSession.new(params[:user_session])
#user_session.save
Authlogic should write the last_login_at and last_login_ip attributes for you. More info in the Authlogic docs under Module: Authlogic::Session::MagicColumns
As for last_login_at and last_login_ip, do you have current_login_at and current_login_ip fields in your table ? last_login_at and last_login_ip are set with the values of current_login_at and current_login_ip before they are reset.
I think the code from the example isn't a really good example.
You shouldn't use #current_user to set the #user variable. Because it won't work if the ApplicationController#current_user method isn't called before show action of the UserController. Basically they are both exactly the same after current_user is called once.
the User Controller should look like this
class UserController < ApplicationController
def show
#user = current_user
end
end
As for the Magic Columns I have no Idea why they don't work for you.
I have set up AuthLogic almost exactly as per the AuthLogic example app at http://github.com/binarylogic/authlogic_example.
After someone logs in as User, they can click on links that send them away into the system and away from the users controller. This is an incredibly noob question, but how can I access that User's ID and other attributes from anywhere else, such as an unrelated view or unrelated controller?
An example of what I'd like to do:
#matchings controller
#matching = Matching.find_by_user_id(user.id)
You can use current_user or #current_user. The function that returns current_user is defined in application controller.
...
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
...
So, you can use:
#matching = Matching.find_by_user_id(current_user.id) or
#matching = Matching.find_by_user(current_user)