I have created an Admin model with the Devise gem. Using the Devise controller generator, I now have a app/controllers/admins folder containing all the stock controllers for me to modify if I choose, such as sessions_controller, passwords_controller, etc.
However, I can't figure out how to just get an Admin controller and simple admin routes like admin_path or new_admin_path.
Here's my rake routes | grep admin
new_admin_session GET /admin/sign_in(.:format) admins/sessions#new
admin_session POST /admin/sign_in(.:format) admins/sessions#create
destroy_admin_session DELETE /admin/sign_out(.:format) admins/sessions#destroy
new_admin_password GET /admin/password/new(.:format) devise/passwords#new
edit_admin_password GET /admin/password/edit(.:format) devise/passwords#edit
admin_password PATCH /admin/password(.:format) devise/passwords#update
PUT /admin/password(.:format) devise/passwords#update
POST /admin/password(.:format) devise/passwords#create
admin_root GET /admin(.:format) admins/sessions#portal
admin_sign_out GET /admin/sign_out(.:format) admin/sessions#destroy
And here are the relevant parts of my routes.rb
devise_for :admins, path: 'admin', controllers: { sessions: 'admins/sessions' }
devise_scope :admin do
get "/admin", to: 'admins/sessions#portal', as: 'admin_root'
get "/admin/sign_out", to: 'admin/sessions#destroy', as: 'admin_sign_out'
end
You'll see that I've currently got a portal method in my Admin::SessionsController, which is my current workaround. I know the right place for that page is in an AdminsController but I can't figure out how to set that up.
Adding admins: 'admins/admins' to the devise_for :admins, controllers: block doesn't give me any new routes. I tried adding an AdminsController with methods but that doesn't help either, trying to go to /admin/new or /admins/new says no route matches.
This is how I have my device and namespaces set up
# config/routes.rb
Rails.application.routes.draw do
devise_for :admins, :controllers => { registrations: 'admins/registrations',
sessions: 'admins/sessions',
passwords: 'admins/passwords',
confirmations: 'admins/confirmations'
}
authenticate :admin do
namespace :admins do
...
root 'dashboards#index'
end
end
...
root 'pages#index'
Now controllers are also important.
#app/controllers/admin_controller.rb
class AdminController < ApplicationController
layout 'admins/application'
before_filter :authenticate_admin!
end
The Devise controllers I have them set like this
#app/controllers/admins/sessions_controller.rb
class Admins::SessionsController < Devise::SessionsController
layout 'admins/application'
...
end
Repeat this process for all your other device controllers
Let me know if this helps you out
Related
I wanted to remove the devise routes, as I built custom routes and controllers for my purpose. However, I noticed that when I delete my devise_for :users line in routes and replace it with only...
Routes file
devise_scope :user do
delete 'logout', to: 'devise/sessions#destroy'
end
I am not getting an error:
View:
<% if user_signed_in? %>
Error:
"undefined method `user_signed_in?' for #<#Class:0x00007f984411aab0:0x00007f98441266f8>"
Why would a helper need routes? Which routes are required for it? Can I define only the helpers?
I also plan to use current_user.present? and sign_in #user, but do not know if they will error later, as I can not get passed the undefined Method.
From the Devise Gem: https://github.com/heartcombo/devise/blob/45b831c4ea5a35914037bd27fe88b76d7b3683a4/lib/devise/rails/routes.rb
# Let's say you have an User model configured to use authenticatable,
# confirmable and recoverable modules. After creating this inside your routes:
#
# devise_for :users
#
# This method is going to look inside your User model and create the
# needed routes:
#
# # Session routes for Authenticatable (default)
# new_user_session GET /users/sign_in {controller:"devise/sessions", action:"new"}
# user_session POST /users/sign_in {controller:"devise/sessions", action:"create"}
# destroy_user_session DELETE /users/sign_out {controller:"devise/sessions", action:"destroy"}
#
# # Password routes for Recoverable, if User model has :recoverable configured
# new_user_password GET /users/password/new(.:format) {controller:"devise/passwords", action:"new"}
# edit_user_password GET /users/password/edit(.:format) {controller:"devise/passwords", action:"edit"}
# user_password PUT /users/password(.:format) {controller:"devise/passwords", action:"update"}
# POST /users/password(.:format) {controller:"devise/passwords", action:"create"}
#
# # Confirmation routes for Confirmable, if User model has :confirmable configured
# new_user_confirmation GET /users/confirmation/new(.:format) {controller:"devise/confirmations", action:"new"}
# user_confirmation GET /users/confirmation(.:format) {controller:"devise/confirmations", action:"show"}
# POST /users/confirmation(.:format) {controller:"devise/confirmations", action:"create"}
devise_for is how Devise hooks into the entire framework and sets up its helpers. So skipping entirely is just not how you want to go about it. If you just want to change the name of the paths use the correct option instead:
# availabe path_names are
# :sign_in, :sign_out, :sign_up, :password, :confirmation, :unlock.
devise_for :users, only: :sessions,
path_names: {
sign_out: 'logout'
}
The skip: and only: options can be used to limit the generated routes:
devise_for :users,
only: :sessions,
path_names: {
sign_out: 'logout'
}
devise_for :users,
skip: [:registrations, :passwords, :confirmations],
path_names: {
sign_out: 'logout'
}
If you really want to use the path /user/... instead of /users/...:
devise_for :users, only: :sessions,
path: 'user',
path_names: {
sign_out: 'logout'
}
Not really something I would recommend though.
See ActionDispatch::Routing::Mapper#devise_for.
I added user profile to Devise from some tutorials I found online and linking to profile works great. However now I'm having problem with user signup.
I assume, since I have created users_controller.rb for user profile, it is looking into users_controller.rb now for all actions. So, regarding the sign up, I added create def, then it asked def new, then def update and so on... Things got really complicated and I got different type of errors!
MY QUESTION:
Is it possible to redirect all actions to the default Devise signup, login, update, ... while I keep user_controllers.rb only for user profile?
Thank you!
route
Rails.application.routes.draw do
resources :users
devise_for :users
devise_scope :user do
get 'register', to: 'devise/registrations#new', as: :register
get 'login', to: 'devise/sessions#new', as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
end
root 'posts#index'
end
Controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
Routing and Controllers in Devise
Your config/routes.rb determines the actions triggered with each GET/POST/PUT/PATCH request made to urls
You can read more about this at the following link
Routing/Controller in Devise
When you configure Devise you set the routes by using the devise_for method in your routes.rb
# config/routes.rb
Rails.application.routes.draw do
devise_for :users
end
to read more about the method devise_for read the documentation
devise_for will generate the following routes:
# Session routes for Authenticatable (default)
new_user_session GET /users/sign_in {controller:"devise/sessions", action:"new"}
user_session POST /users/sign_in {controller:"devise/sessions", action:"create"}
destroy_user_session DELETE /users/sign_out {controller:"devise/sessions", action:"destroy"}
# Password routes for Recoverable, if User model has :recoverable configured
new_user_password GET /users/password/new(.:format) {controller:"devise/passwords", action:"new"}
edit_user_password GET /users/password/edit(.:format) {controller:"devise/passwords", action:"edit"}
user_password PUT /users/password(.:format) {controller:"devise/passwords", action:"update"}
POST /users/password(.:format) {controller:"devise/passwords", action:"create"}
# Confirmation routes for Confirmable, if User model has :confirmable configured
new_user_confirmation GET /users/confirmation/new(.:format) {controller:"devise/confirmations", action:"new"}
user_confirmation GET /users/confirmation(.:format) {controller:"devise/confirmations", action:"show"}
POST /users/confirmation(.:format) {controller:"devise/confirmations", action:"create"}
If the client/browser performs a GET request to the server via the url /users/sign_in, the server will execute the action new from the controller in the folder app/controllers/devise/sessions
That is where my views are generated and even if I do not have the devise controller, this is how it is mapped.
You can override this behavior, as explained in this guide, bu using the following syntax:
devise_for :users, controllers: { sessions: 'users/sessions' }
This means that for sessions you are going to use the controller located in the folder app/controllers/users/sessions and not devise/sessions.
You can test this, generate your routes and see how you will have the routes for every action.
The Best Practice
The best practice as suggested from Devise is just generating the devise controller, which will have actions that are already wired with the Devise routing. Each action from the controller will call with super the Devise controller actions, if you want to enhance or change that logic you can do it by reading the Devise API
To that controller you can add any action you want then configure appropriately your routing
For routing and controller info from ruby on rails guide
http://guides.rubyonrails.org/action_controller_overview.html
http://guides.rubyonrails.org/routing.html
I have the following code in routes.rb:
devise_for :admin_users, controllers: {
registrations: 'tap/registrations',
sessions: 'tap/sessions',
passwords: 'tap/passwords',
confirmations: 'tap/confirmations'
}
The code above creates the following routes:
new_admin_user_session GET /admin_users/sign_in(.:format) tap/sessions#new
admin_user_session POST /admin_users/sign_in(.:format) tap/sessions#create
destroy_admin_user_session DELETE /admin_users/sign_out(.:format) tap/sessions#destroy
admin_user_password POST /admin_users/password(.:format) tap/passwords#create
new_admin_user_password GET /admin_users/password/new(.:format) tap/passwords#new
edit_admin_user_password GET /admin_users/password/edit(.:format) tap/passwords#edit
PATCH /admin_users/password(.:format) tap/passwords#update
PUT /admin_users/password(.:format) tap/passwords#update
For some reason, the registrations and confirmations controller are not appearing in routes. How do I fix this?
inside model (admin_users.rb) have you added this line below, probably this link can help you more
devise :database_authenticatable, :registerable
I have a rails api application where I am using Devise gem for user management. I created a user model off the devise gem. After that, I noticed that I have two same routes listed in the rake routescommand. I want POST (/users) to call api/v1/users#create action first and then call devise/registrations#create.
user_registration POST /users(.:format) devise/registrations#create
api_users POST /users(.:format) api/v1/users#create {:format=>:json}
When I test POST (/users) using users_controller_spec file, api/v1/users#create action is called. However, when I do a POST (/users) using POSTMAN, the logs indicates that devise/registrations#createaction is called instead.
How do I correct this so that the POST (/users) I do using POSTMAN or curl calls api/v1/users#create first to create the user model and then calls devise/registrations#create to register the user?
I am not 100% sure how devise works so any help here would be helpful.
This is my config/routes.rb
Rails.application.routes.draw do
devise_for :users
# Api definition
namespace :api, defaults: { format: :json }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# We are going to list our resources here
resources :users, only: [:show, :create, :update, :destroy]
resources :sessions, only: [:create, :destroy]
end
end
end
So, the thing with Rails Routes is, when you make a request, routes are checked as they are defined in the routes.rb from top to bottom.
Now, when you make a request via POSTMAN, the /users path matches with a path generated via devise_for, as it is the first line in the file.
Now, when you are writing tests for the controller, you are not really accessing /users, you are just telling the api/v1/users_controller to invoke the create method, which is bound to hit the api/v1/users#create
Now, a way you can resolve this conflict is by changing what devise names its routes. If you do something like this:
Rails.application.routes.draw do
devise_for :users, path: 'customer'
# Api definition
namespace :api, defaults: { format: :json }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# We are going to list our resources here
resources :users, only: [:show, :create, :update, :destroy]
resources :sessions, only: [:create, :destroy]
end
end
end
This is what the devise routes will be:
new_user_session GET /customer/sign_in(.:format) devise/sessions#new
user_session POST /customer/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /customer/sign_out(.:format) devise/sessions#destroy
user_password POST /customer/password(.:format) devise/passwords#create
new_user_password GET /customer/password/new(.:format) devise/passwords#new
edit_user_password GET /customer/password/edit(.:format) devise/passwords#edit
...
I'm trying to make an app in Rails 5.
In order to keep the file structure neat, I'm trying to make folders inside my controllers directory, that I can use to group similar resources.
For example, I have:
app/controllers/users/users_controller.rb
I can then make my sessions controller nested inside the controllers/users director so that all resources relating to the user are grouped under the user folder.
I'm stuck though for what to do with my routes file.
When I rake routes, I can see:
users#index {:controllers=>{:users=>"users/users"}}
POST /users(.:format) users#create {:controllers=>{:users=>"users/users"}}
new_user GET /users/new(.:format) users#new {:controllers=>{:users=>"users/users"}}
edit_user GET /users/:id/edit(.:format) users#edit {:controllers=>{:users=>"users/users"}}
user GET /users/:id(.:format) users#show {:controllers=>{:users=>"users/users"}}
PATCH /users/:id(.:format) users#update {:controllers=>{:users=>"users/users"}}
PUT /users/:id(.:format) users#update {:controllers=>{:users=>"users/users"}}
DELETE /users/:id(.:format) users#destroy {:controllers=>{:users=>"users/users"}}
In my routes file, I've tried a few things (set out below) - none of them work:
Rails.application.routes.draw do
devise_for :users,
:controllers => {
:sessions => 'users/sessions',
:registrations => "users/registrations",
:omniauth_callbacks => 'users/omniauth_callbacks'
}
resources :identities,
:controllers => {
:identities => 'users/identities'
}
resources :users do
scope module: :users do
resources :users
end
end
root 'home#index'
end
I have also tried:
resources :users,
:controllers => {
:users => 'users/users'
}
Each time, I get an error that says:
ActionController::RoutingError at /users
uninitialized constant UsersController
I don't know what I need to do to get this working. I have changed each of my controllers that is nested inside the controllers/users folder with a prefix of:
Users::
Can anyone see how to set this up so that I can keep my files neatly organised?
Note: I haven't created the same file directory structure in my models folder. I want to - but I'm concerned that I'm not able to figure this out for the controllers. Can anyone see what I'm doing wrong?
I recommend you put the actual users_controller in the base controllers directory, and only put the nested controllers inside the users directory (ie follow the same structure as the nesting).
The alternative is to name the users_controller the way that rails is expecting ie inside the Users module:
module Users
class UsersController < ApplicationController
...
and then refer to it with: Users::UsersController
I always find the duplication in the name a bit cumbersome, and prefer top-level controllers to just be in the base directory.
I also really like the approach explained in this video:
https://youtu.be/1B0Vioz4Ukw
Basically he creates BaseController for each Module, those inherit from ApplicationController and then the related controllers inherit directly from their corresponding BaseController.
Example used in video
Controllers
Controllers are namespaced, in the example with Auth. Then a base class is made, derived from ApplicationController. The base class is then used for all other of the Auth controllers.
# app/controllers/auth/base_controller.rb
class Auth::BaseController < ApplicationController
end
# app/controllers/auth/posts_controller.rb
class Auth::PostsController < Auth::BaseController
...
end
# app/controllers/auth/sessions_controller.rb
class Auth::SessionsController < Auth::BaseController
...
end
# app/controllers/auth/users_controller.rb
class Auth::UsersController < Auth::BaseController
...
end
Routes
Resources are namespaced while simple routes have auth/ added to their routes.
# config/routes.rb
namespace :auth do
resource :confirmation
resource :session, only: [:create]
resources :users, only: [:create]
end
get 'login' => 'auth/sessions/new'
delete 'logout' => 'auth/sessions#destroy'
get 'register' => 'auth/users#new'
Views
Views are now namespaced under auth.
app/views/auth/sessions/*
app/views/auth/posts/*
app/views/auth/users/*
Paths
Paths will also be under auth namespace to work. Check rails routes for details.
Example route: auth_session_path
Example route from set of objects: [:auth, #user]