I am working on a Rails project using single table inheritance and devise.
I have User, Client and Guide models. Client and Guide inherit from the User model. They have different paths for registration but I want them both to be able to log in on the same page.
Currently, neither can log in because I believe the resource is being set to User.
Does anyone knew how I can test a User login to see what type it is (either Client or Guide) and then set it to be that resource and direct it to the correct page?
This is my route:
Rails.application.routes.draw do
get 'pages/index'
devise_for :clients, controllers: {registrations: "clients/registrations", confirmations: "clients/confirmations", omniauth: "clients/omniauth", unlocks: "clients/unlocks"}, :skip => [:sessions, :passwords]
devise_for :guides, controllers: {registrations: "guides/registrations", confirmations: "guides/confirmations", omniauth: "guides/omniauth", unlocks: "guides/unlocks"}, skip: [:sessions, :passwords]
# devise_for :users, controllers: {passwords: "devise/passwords", sessions: "devise/sessions"}, skip: [:registrations, :confirmations, :omniauth, :unlocks]
devise_for :users, controllers: {passwords: "devise/passwords"}, skip: [:sessions, :registrations]
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'pages#index'
Here is my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :type, inclusion: { in: %w(Client Guide)}
####################################################################################
# make sure that guide information is verfied as only being for a guide
validate :only_a_guide_has_bio
validate :only_a_guide_has_availability
validate :only_a_guide_has_years_in_business
validate :only_a_guide_has_certified
validate :only_a_guide_has_location
def only_a_guide_has_bio
if self.bio
self.errors.add(:bio, "Only a guide can have a home.") unless self.guide?
end
end
def only_a_guide_has_availability
if self.availability
self.errors.add(:availability, "Only a guide can have availability.") unless self.guide?
end
end
def only_a_guide_has_years_in_business
if self.years_in_business
self.errors.add(:years_in_business, "Only a guide can have years in business.") unless self.guide?
end
end
def only_a_guide_has_certified
if self.certified
self.errors.add(:certified, "Only a guide can be certified.") unless self.guide?
end
end
def only_a_guide_has_location
if self.location
self.errors.add(:location, "Only a guide can have location.") unless self.guide?
end
end
def first_name
self.first_name
end
####################################################################################
def guide?
self.type == 'Guide'
end
def client?
self.type == 'Client'
end
end
Here is my Guide model:
class Guide < User
has_many :trips
end
Here is my Client model:
class Client < User
has_and_belongs_to_many :trips
def new
super
end
end
Here is the User sessions controller:
class Users::SessionsController < Devise::SessionsController
include ApplicationHelper
def create
super
end
def new
super
end
def destroy
super
end
end
The super for devise create
is:
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
(more here https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb)
And this is the new session view:
.page-header
%h1= t ".title", default: "Login"
= form_for(resource, url: new_user_session_path, html: {class: "form-horizontal form-user"}) do |f|
.form-group
= f.label :email, class: "control-label col-md-4"
.col-md-8
= f.email_field :email, class: "text-field form-control", html: {spellcheck: "false"}
.form-group
= f.label :password, class: "control-label col-md-4"
.col-md-8
= f.password_field :password, class: "text-field form-control", html: {autocomplete: "off"}
- if devise_mapping.rememberable?
.form-group
= f.label :remember_me, "Remember Me", class: "control-label col-md-4"
.col-md-8
= f.check_box :remember_me
.form-group
.col-md-offset-4.col-md-8
= f.submit "Login", class: "btn btn-primary"
.form-group
.col-md-offset-4.col-md-8
= render "users/shared/links"
Changing
SessionsController#create
to the following works for me:This assumes User is the superclass and that instances of both subclasses log in using their e-mail addresses. If they use different attributes, you can check for them by looking at
request.params
and appropriately modifying#create
.EDIT: I also needed to add
controllers: { sessions: 'sessions' }
to the relevant routes:If this doesn't work, you may want to check out this thread for some ideas.