I have a resource, say Product
, which can be accessed by two different user classes: say Customer
and Admin
. There is no inheritance between these two.
I am using Devise for authentication:
# config/routes.rb
devises_for :customers
deviser_for :admins
I have these two controllers:
# app/controllers/customers/products_controller.rb
class Customers::ProductsController < ApplicationController
and
# app/controllers/admins/products_controller.rb
class Admins::ProductsController < ApplicationController
Now depending on who logs in (Customer
or Admin
), I want products_path
to point to the corresponding controller. And I want to avoid having customers_products_path
and admins_products_path
, that's messy.
So I have setup my routes as such
# config/routes.rb
devise_scope :admin do
resources :products, module: 'admins'
end
devise_scope :customer do
resources :products, module: 'customers'
end
This doesn't work. When I login as a Customer
, products_path
still points to Admins::ProductsController#index
as it is the first defined.
Any clue? What I want to do might simply be impossible without hacking.
UPDATE According to the code, it is not doable.
It turns out the best way to achieve that is to use routing constraints as such:
# config/routes.rb
resources :products, module: 'customers', constraints: CustomersConstraint.new
resources :products, module: 'admins', constraints: AdminsConstraint.new
# app/helpers/customers_constraint.rb
class CustomersConstraint
def matches? request
!!request.env["warden"].user(:customer)
end
end
# app/helpers/admins_constraint.rb
class AdminsConstraint
def matches? request
!!request.env["warden"].user(:admin)
end
end
I stored the constraint objects in the helper folder because I don't really know the best place to put them.
Thanks to @crackofdusk for the tip.
For this you will need to override the existing after_sign_in_path_for method of devise. Put this in your app/controllers/application_controller.rb
def after_sign_in_path_for(resource)
if resource.class == Admin
'/your/path/for/admins'
elsif resource.class == Customer
'/your/path/for/customers'
end
end
Note: If you want to implement previous_url
that was requested by user then you can use it like this:
session[:previous_url] || 'your/path/for/admins'
来源:https://stackoverflow.com/questions/20492370/rails-devise-same-resource-different-controller-based-on-user-type