I have two models, User and Admin. I want to know if its possible to override Devise's Registrations Controller and have two custom Registrations Controllers - one for each model.
I know its possible to get what I want by overriding the registrations controller and I would just use If else statements although (correct me if I am wrong) I believe its better to avoid having many if else statements if possible.
You can see what I've done so far on another post - I have scoped views and it uses the wrong set of views for some reason. Devise Views with Multiple Models
Yes it's possible
Routes
When you generate Devise for both models, you'll have to add it to your routes:
#config/routes.rb
devise_for :users
devise_for :admin
Devise actually uses these routes to populate its arguments, one of which is the controllers argument:
#config/routes.rb
devise_for :users, controllers: { sessions: "sessions", registrations: "registrations" }
devise_for :admin, controllers: { registrations: "admin/session" }
Controllers
This will allow you to create controllers to override the Devise default ones:
#app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
#Your Code Here
end
#app/controllers/admin/registrations_controller.rb
class Admin::RegistrationsController < Devise::RegistrationsController
#Your Code Here
end
Related
I know this question was asked several times. I tried them but none of them solved my problem. I used devise for users. I want to redirect the user to a different page on sign up rather than signing in directly. I created registration controller, and tried overriding "after_inactive_sign_up_path_for" but it didn't work out. I 'm using devise confirmable also. So until the user is verified I should redirect him to other page.
registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
"http://www.google.com" # Or :prefix_to_your_route
end
end
routes.rb
devise_for :users, controllers: { registrations: "registrations" }
Next I tried to move the registrations into a different folder in the controllers folder. But that too didn't work out.
FYI: I'm using rails 5 and devise 4.2.1.
Your controller should be in the path app/controllers/users/registrations_controllers.rb.
Then your routes should be
devise_for :users, controllers: { registrations: "users/registrations" }
So I've been chewing on this problem for a few days now and couldn't come up with an eloquent solution. There are a few resources online but they are not very comprehensive.
Here is my situation:
Specs
Rails app that requires authentication of three different user types
(Admin, Manager, Client)
They have very different attributes
When they sign in they are led to very different views
Would like to maintain a single login form
Each user type is restricted to their own controller and it's actions
Clients can sign themselves up via homepage
Admins and Managers can only be created from the Admin portal
Currently, I have Devise set up with a User class, and a polymorphic relationship between User and Admin, Manager, Client. I tried using a single table inheritance, but due to the fact that each user type has varying attributes I wanted to avoid a large single table with many null values.
Proposed solutions
Polymorphic Relationship. User model, with Admin, Manager and Client
inheriting. The question here is how can I restrict each model to
their respective controller?
User model, with a has_one relationship
with a ClientProfile and ManagerProfile to handle the extra
attributes. Then use declarative_authorization or CanCanCan to
restrict authorizations. This helps keep the app DRY by keeping only
one User model, but view logic then gets complicated.
Of the two solutions, which one seems more scalable, clean and secure? If there are any other suggestions for general app architecture that would be better? Thanks!
This is how I have my app set up form multiple user types on ROR
#config/routes.rb
AppName::Application.routes.draw do
devise_for :users, :controllers => {
registrations: 'users/registrations',
:sessions => "users/sessions",
:passwords => 'users/passwords',
:confirmations => 'users/confirmations'
}
authenticate :user do
namespace :users do
....
get '/' => 'dashboards#index'
root :to => 'dashboards#index'
end
end
devise_for :admins, :controllers => {
:sessions => "admins/sessions",
:passwords => 'admins/passwords',
:confirmations => 'admins/confirmations'
}
authenticate :admin do
namespace :admins do
....
get '/dashboard' => 'dashboards#index
root :to => 'dashboards#index'
end
end
root 'pages#index'
end
Now that you have your routes in place you can create your controllers
I have
#app/controllers/user_controller.rb
class UserController < ApplicationController
before_filter :authenticate_user!
layout 'users/default'
before_filter :check_user_active
private
def check_user_active
unless current_user.active
flash[:notice]= t(:user_not_active)
sign_out current_user
redirect_to new_user_session_path
end
end
end
# app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
layout 'users/login'
def create
end
def destroy
end
end
Last but not least is the views
just put everything in its name space like #app/views/users/sessions/new.html.haml
I have the devise SessionController overwrited:
on app/controllers/customers/sessions_controller.rb
class Customers::SessionsController < Devise::SessionsController
before_filter :destroy_cart, only: :destroy
def destroy_cart
cart = Cart.find(current_client.cart.id)
cart.destroy
end
end
but the cart is never destroyed, even if I overwrite the destroy method directly and add the super after my code, the cart its still there, in the database (I knkow I could create the cart just once and get it when the user logs in again or create a new one when he use the app for first time, but I want to try it this way for now), is like if is not reading my code on that SessionController.
and for some reason even when I have my views this way:
app/views/customer/registrations
the changes that I do on that views are only reflected if I change it to
app/views/devise/registrations
my routes.rb is:
devise_for :clients, :controllers => { sessions: 'customers/sessions'}
devise_scope :client do
root to: "customers/Sessions#new"
end
the model that I am using with devise is Client
why I cant destroy the cart in the devise controller?
and why I cant use the views/customer/sessions if the documentation it says I can/have to do it?
thank you for reading.
you can always try to do
def destroy
cart = Cart.find(current_client.cart.id)
cart.destroy
super
end
but first you might want to ensure that you really overwritten devise's controller correctly.
The reason why you can't see changes done to app/views/customer/registrations is beacuse you seems to overwrite only :sessions controller, so you need to change
devise_for :clients, :controllers => { sessions: 'customers/sessions'}
to
devise_for :clients, :controllers => { registrations: 'customers/registrations', sessions: 'customers/sessions'}
The last question is:
" why I cant use the views/customer/sessions if the documentation it says I can/have to do it? "
You have a typo here, you are using customers namespace, not customer in routes.rb [ sessions: 'customers/sessions' ] - just a typo?
Watch your spelling. The before_filter is calling a method that doesn't exist.
I am using Devise with multiple models (three to be exact) and each role has some different interactions. For example, after the user model signs up I override a devise method to redirect them to a specific welcome path, where with the employer model I take them to a credit card form, etc.
As a result, I need to namespace everything. Namespacing the views and controllers are not tough, however, I was wondering if there is a way to namespace the controllers without having to specify EVERY devise controller.
For example, is there a way to basically do this:
devise_for :employers, :controller => "employers"
Instead of having to do this:
devise_for :employers, :controllers => {
:registrations => "employers/registrations",
:sessions => "employers/sessions",
:confirmations => "employers/confirmations",
:passwords => "employers/passwords",
:unlocks => "employers/unlocks",
:mailer => "employers/mailer"
}
Might seem trivial but when you have three models to maintain it could get a bit much.
Take a look at the following answer from Devise within namespace. Simply namespacing in the routes.rb will not produce the desired results. You'll have to generate controllers for each action you want. For sessions for example, You will have to create a new controller called Sessions in the controller Employer namespace:
bundle exec rails g controller employer/sessions
then subclass the new session controller from devise session controller to bring in all the Devise methods required to properly handle sessions:
class Employer::SessionsController < Devise::SessionsController
end
and change your 'devise_for :employers, :controller => "employers"' line in config/routes.rb to the following:
devise_for :employers, :controllers => { :sessions => "employer/sessions" }
Finally, as an optional step, you can generate views to app/views/employer/sessions directory. You can do this my setting "config.scoped_views = true" inside config/initializers/devise.rb and running the following to generate views scoped to employers:
rails generate devise:views users
This should generate templates at app/views/employer/sessions/new. Otherwise, the new session controller will just use the default view templates.
Hope this helps!
Will it work by just saying: devise_for :employers, :path => "employers" ?
I'm working on a rails site using devise, where we do not want user sign ups just yet. User authentication is so we can login to access restricted parts of the site and add/edit things as we see fit. So for now, I created the following controller:
class Users::RegistrationController < Devise::SessionsController
def new
end
end
And setup my routes in this fashion:
devise_for :users, :controllers => { :registration => "users/registration" }
However, when I run rake routes, I still see a returned value for the create action on the registration controller. Any ideas on how to get rid of it?
Try using :registrations instead of :registration. Also, it seems like your custom controller class should be defined via:
class Users::RegistrationsController < Devise::RegistrationsController