I have the following error when trying to log out from an authentication gem I have just install.
http://0.0.0.0:3000/users/sign_out
Routing Error
uninitialized constant UsersController
I dont have a users_controller.rb file.
I do have a user.rb Model.
This is the path/url i am trying to reach:
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
Any idea?
Your problem is that devise_for :users is overshadowed by resources :users
Rails complains about UsersController, because it thinks that you are trying to reach users#show (if you use GET request) and users#destroy (if you use DELETE)
You should either create UsersController or remove resources :users from your routes. And if you decide to create UserController, move resources :users under devise_for :users. Devise routes would take precedence that way.
Are you sure that you need resources :users?
Related
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'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]
Currently I have a typical devise installation but I've added user-id's to the routes that have a current_user available. But I'm getting the error stated below when trying to login to the service.
Error
NameError in Devise::SessionsController#create
undefined local variable or method `offers_path' for #<Devise::SessionsController:0x007f0e80ec7a08>
def after_sign_in_path_for(resource)
offers_path
end
routes.rb
devise_for :users
resources :users do
resources :offers do
member do
put :tourcomplete
end
end
end
Rake Routes
tourcomplete_user_offer PUT /users/:user_id/offers/:id/tourcomplete(.:format) offers#tourcomplete
user_offers GET /users/:user_id/offers(.:format) offers#index
POST /users/:user_id/offers(.:format) offers#create
new_user_offer GET /users/:user_id/offers/new(.:format) offers#new
edit_user_offer GET /users/:user_id/offers/:id/edit(.:format) offers#edit
user_offer GET /users/:user_id/offers/:id(.:format) offers#show
PATCH /users/:user_id/offers/:id(.:format) offers#update
PUT /users/:user_id/offers/:id(.:format) offers#update
DELETE /users/:user_id/offers/:id(.:format) offers#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
Not too sure if this is the best way to put user-id's within the logged in routes. Would love any help on this issue.
Seeing your routes, you don't have any offers_path but you are trying to use that, that's why you are getting this error.
You should try using user_offers_path instead and the pass the user as the argument, something like this:
user_offers_path(#user)
Let's see if I can explain myself well enough about the doubts I have.
I have a User model that is managed by Devise. So in my routes I have:
devise_for :users
In the User model, I have an association with the model Plan. The assocation is:
User has_many Plans
Plan belongs_to User
At this point I also have a resource for the Plan model, so I can fetch all the Plans, show a particular plan and so on. But I want to go further.
I want to be able to see plans of a particular User and let a particular User to see his own plans and edit them.
So, for example, whenever I go to:
/users/:id/plans
I want to be able to see the plans for that particular :id user. And if the user who is visiting that url is the one that is logged in, I want him to be able to edit those plans.
How can I manage all this behavior? Is there any gem out there that helps with it? Or I need to do conditionals in the views saying if current_user...
Let's start with routes, you can make your routes like this:
resources :users do
resources :plans, only: [:index]
end
resources :plans, except: [:index]
I used resources :plans inside resources :users to have route like this /users/:user_id/plans, while the resources :plans outside is for the rest of the actions (edit, destroy, ...) that don't require a user_id, i.e., a plan is identified by a unique id so you don't need a user_id to fetch it from the db for editing or destroying.
Now for the controller, we can make it like this:
class PlansController < ApplicationController
before_filter :is_plan_owner?, only: [:edit, :update]
def index
#plans = Plan.where(:user_id => params[:user_id])
end
def edit
#plan = Plan.find(params[:id])
end
private
def is_plan_owner?
if current_user != Plan.find(params[:id]).user
# Scream, shout, call 911 and/or redirect else where
end
end
end
This is no different than using any other nested resource. The call to devise_for in the routes.rb file does not provide RESTful routing to the user model. Think about it without the nested resource for a minute, with just a standard Devise install. If you were to rake routes you would get something similar to the following:
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
user_password POST /users/password(.:format) devise/passwords#create
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
sign_in GET /sign_in(.:format) devise/sessions#new
This provides nothing for indexing or showing users, so you would still need to add routes for that:
resources :users, only: [:index, :show]
Now you get:
users GET /users(.:format) users#index
user GET /users/:id(.:format) users#show
Ok, now we're getting somewhere, then it's simply adding the nested resource, all the while Devise pays it no mind.
resources :users, only: [:index, :show] do
resources :plans
end
Which gives you the resourceful routing you desire
user_plans GET /users/:user_id/plans(.:format) plans#index
POST /users/:user_id/plans(.:format) plans#create
new_user_plan GET /users/:user_id/plans/new(.:format) plans#new
edit_user_plan GET /users/:user_id/plans/:id/edit(.:format) plans#edit
user_plan GET /users/:user_id/plans/:id(.:format) plans#show
PUT /users/:user_id/plans/:id(.:format) plans#update
DELETE /users/:user_id/plans/:id(.:format) plans#destroy
And that's really all there is to it. Devise stays out of your way on this one.
I'm trying to nest some routes under the namespace, account.
I want user management under account like /account/users and /account/users/5/edit
In routes.rb:
namespace :account do
resources :users do
member do
put 'generate_api_key'
end
collection do
post 'api_key'
end
end
end
My controllers are not namespaced or put them in any different directory.
/app
/controllers
accounts_controller.rb
users_controller.rb
In my development environment this is working fine, but in production I get 404 responses to any of the /account/users... paths (which, by the way, are all still generated correctly: new_account_users_path, edit_account_user_path, etc).
rake routes generates the same output in both environments. Here is the relevant bit:
generate_api_key_account_user PUT /account/users/:id/generate_api_key(.:format) {:action=>"generate_api_key", :controller=>"account/users"}
api_key_account_users POST /account/users/api_key(.:format) {:action=>"api_key", :controller=>"account/users"}
account_users GET /account/users(.:format) {:action=>"index", :controller=>"account/users"}
POST /account/users(.:format) {:action=>"create", :controller=>"account/users"}
new_account_user GET /account/users/new(.:format) {:action=>"new", :controller=>"account/users"}
edit_account_user GET /account/users/:id/edit(.:format) {:action=>"edit", :controller=>"account/users"}
account_user GET /account/users/:id(.:format) {:action=>"show", :controller=>"account/users"}
PUT /account/users/:id(.:format) {:action=>"update", :controller=>"account/users"}
DELETE /account/users/:id(.:format) {:action=>"destroy", :controller=>"account/users"}
Given that the routes seem to look for the Users controller in the /account subdirectory, I suppose my question is why does this work in development?
Production is:
Rails 3.0.7
Passenger
Apache
Development is:
Rails 3.0.7
Mongrel
Thanks for your thoughts on this one.
If you're namespacing like this, Rails requires the controllers to be at their correct paths, such as app/controllers/account/users_controller.rb. If you don't want to do this, then use scope instead:
scope :path => "account" do
resources :users
end