I was wondering what the best way to add extra functionality to Devise's session#destroy action would be.
To give some context, I'm making a website where Users have Carts, and when the user's session either times out or he logs out, I want his Cart to be labeled as inactive.
I found this but I'm a bit hesitant to override the Devise controller, as it seems a bit messy...
Are there any other ways to set this Cart to inactive when a user's session is destroyed?
I do suggest you derive your controller from devise and hook onto the action, so you can safely keep away from devise's internals.
# routes.rb
devise_for :users, :controllers => { :sessions => "sessions" } # etc
# sessions_controller.rb
class SessionsController < Devise::SessionsController
after_filter :set_cart_inactive!, :only => :destroy
def set_cart_inactive!
unless user_signed_in? # logout successful?
# code here
end
end
end
Related
I'm new to Rails & I have a page where the user can cancel their account using button_to. However, how can I redirect the user to another page? For example, I would want to redirect to a post-cancellation page saying "goodbye" upon deletion of their account.
I tried looking to see if there was an after_user_deletion devise method to override but there is not.
Here is what I have in the edit view under registrations from Devise.
<div id="modal">
<p>Are you sure you want to cancel?</p>
<%= button_to "Yes", registration_path(resource_name), method: :delete%>
<button>No</button>
</div>
Im not sure if it's necessary to make another controller but I made a cancellation controller.
class CancellationController < ApplicationController
before_action :authenticate_user!
def cancel
end
end
Here is what I have in routes.rb
Rails.application.routes.draw do
devise_for :users
root to: 'dashboard#index'
devise_scope :user do
get 'cancel' => 'cancellation#cancel'
end
end
Here is the contents of rake routes:
RakeRoutes
You need to override the redirect on the devise registration controller:
# config/routes.rb
devise_for :users, controllers: { registrations: 'registrations' }
This will point to your controllers folder now! So you need to create your own devise controller:
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
# DELETE /resource
def destroy
# your own logic here, so you can redirect it after
end
def cancel
expire_data_after_sign_in!
redirect_to here_your_url
end
end
Note: You are using the DELETE on your button, that triggers the destroy not the cancel, you can check the devise controller here.
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'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
I've seen similar posts already, but couldn't quite get the answer I needed.
I have a User model and using STI a Student model that is a type of User.
When I create a new Student, Devise logs in that Student with a student_session. The problem is the rest of my app uses a user_session. SO, should I create a new user_session using the student_session? and then logout the student?
Or is there a way to get Devise to allow a student creation, but login as the User base model?
Thank you,
Anthony
Check out this post and see if it helps you:
Rails: Using Devise with single table inheritance
The summary is essentially to do the following:
config/routes.rb:
devise_for :users, :controllers => { :sessions => 'sessions' }, :skip => :registrations
devise_for :students, :skip => :sessions
app/controllers/sessions_controller.rb:
class SessionsController < Devise::SessionsController
def create
rtn = super
sign_in(resource.type.underscore, resource.type.constantize.send(:find, resource.id)) unless resource.type.nil?
rtn
end
end
For this you have to create a custom controller.
Just an example implementation
#POST create
def create
student = Student.create(params[:student]) # normal crud with whatever your form params
sign_in(User.find(student.id)) # this actually signs in the user
# now redirect the student to the dash board / whatever page manually
end