39

I'm trying to split my rails project in a front-end for regular users and a back-end for admins. Therefore i have created a namespace 'admin' so that i can easily control admin specific controller methods/layouts/authentication in the map admin.

I'm using Devise to register/authenticate my admins only. Because it is only used for admins only i'm trying to move Devise to the admin namespace.

I could not find exactly what i was looking for in the documentation of Devise but i tried something like this in routes.rb:

namespace 'admin'do 
  devise_for :admins
end

I also tried to make a custom Devise::Sessions controller but that too didn't seem to work out.

Does anyone know how to do this? Should i just use the regular routes for devise with a custom(admin) layout?

Harm de Wit
  • 2,150
  • 2
  • 18
  • 24

6 Answers6

44

Simply "moving" Devise to the admin namespace is wrong. Devise uses controllers like Devise::SessionsController and that cannot be "moved".

I usually create my own controllers and inherit them from Devise:

class Admin::SessionsController < ::Devise::SessionsController
  layout "admin"
  # the rest is inherited, so it should work
end

And configure this in config/routes.rb:

devise_for :admins, :controllers => { :sessions => "admin/sessions" }

Or you could change the layout only, by making the layout a bit more complex:

class ApplicationController < ActionController::Base

  layout :layout

  private

  def layout
    if devise_controller? && devise_mapping.name == :admin
      "admin"
    else
      "application"
    end
  end

end
iain
  • 16,204
  • 4
  • 37
  • 41
  • Thanks a lot for your comprehendable solution. I'm learning to work with namespaces so your first solution is really helpful to me. – Harm de Wit Dec 21 '10 at 20:00
  • There is no need to inherit every devise controller in order to set your own layout. You can do this in application.rb https://stackoverflow.com/a/11085724/1474934 – secador de pelo Mar 11 '20 at 11:12
29

How about just moving the devise_for method into a scope:

scope '/admin' do
  devise_for :admins
end

With namespace, the controllers will try to look for an Admin::SessionController that wont exist. With scope it doesn't, so that should work.

Enrico Carlesso
  • 6,818
  • 4
  • 34
  • 42
Jack Chu
  • 6,791
  • 4
  • 38
  • 44
  • 1
    Exactly what I was after, Cheers! – Michael De Silva Apr 15 '16 at 13:15
  • Just want to add the link to the documentation on the various ways to add scope to rails routes [here](https://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing) – Vic Aug 31 '19 at 03:22
15

How about specifying devise the path to take, place this outside your namespace.

devise_for :users, path: 'admins'

This will generate the following routes

new_user_session          GET      /admins/sign_in(.:format)          devise/sessions#new
user_session              POST     /admins/sign_in(.:format)          devise/sessions#create
destroy_user_session      DELETE   /admins/sign_out(.:format)         devise/sessions#destroy
user_password             POST     /admins/password(.:format)         passwords#create
new_user_password         GET      /admins/password/new(.:format)     passwords#new
edit_user_password        GET      /admins/password/edit(.:format)    passwords#edit
                          PUT      /admins/password(.:format)         passwords#update
cancel_user_registration  GET      /admins/cancel(.:format)           registrations#cancel
user_registration         POST     /admins(.:format)                  registrations#create
new_user_registration     GET      /admins/sign_up(.:format)          registrations#new
edit_user_registration    GET      /admins/edit(.:format)             registrations#edit
                          PUT      /admins(.:format)                  registrations#updat
                          DELETE   /admins(.:format)                  registrations#destroy

You don't have to change anything in that case, if this is what you are looking for.

Happy Coding :)

Abhinay
  • 1,796
  • 4
  • 28
  • 52
Ronak Jain
  • 1,723
  • 2
  • 24
  • 35
5

Both Jack Chu and iain solutions should solve the problem plus generating your views in order to customize the layout of the login form.

So in your config/routes.rb you should have

scope '/subfolder' do
   devise_for :admins, :controllers => { :sessions => "subfolder/sessions" }
end

namespace :subfolder do
  match '/', :to => 'subcontroller#action'
end

Remember di create your own controllers for sessions as you are already doing. Probably you will need to generate your views, too by using rails generate devise:views

Check this for any doubt on devise tasks.

Community
  • 1
  • 1
Saldan
  • 51
  • 1
  • 2
1

If you want to put your devise views in views/admin/admins/ and your controllers in controllers/admin/admins/:

your sessions_controller.rb in controllers/admin/admins:

class Admin::Admins::SessionsController < ::Devise::SessionsController
  layout "admin/connection"
end

routes.rb :

namespace :admin do |admin|
    devise_for :admins, :controllers => { :sessions => "admin/admins/sessions" }
end

Generating devise views :

rails g devise:views admin/admins
0

In addition to the first solution of the answer of iain i had to generate views of devise or else it throws a template missing exception.

generate views with

rails g devise_views

The views will be located in views>devise. Move the created map 'sessions' to the map views>admin

Harm de Wit
  • 2,150
  • 2
  • 18
  • 24