I'm trying to add a custom action to Devise's registration controller which allows users to change their passwords (I cannot use default registrations#edit since I need a form for changing only password, not email/username). The implementation I wrote below works in development mode but I get this error when I test controller.
Failure/Error: get 'password'
ActionController::RoutingError:
No route matches {:controller=>"registrations", :action=>"password"}
There is my code (I tried to skip unrelated parts)
spec/controllers/registrations_controller_spec.rb
describe RegistrationsController do
include Devise::TestHelpers
before :each do
request.env["devise.mapping"] = Devise.mappings[:users]
end
describe "GET 'password'" do
it "..." do
# The problem is here,
get 'password' # it raises ActionController::RoutingError
end
end
end
app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
# ...
def password
end
end
config/routes.rb
devise_for :users, path: 'account',
controllers: { registrations: 'registartions' },
skip: [:registartions, :sessions]
devise_scope :user do
# ...
scope '/account' do
get 'password' => 'devise/registartions#password', as: "change_password
do
end
spec_helper.rb
# ...
RSpec.configure do |config|
# ...
config.include Devise::TestHelpers, :type => :controller
end
I would usually add a comment for this, but I'm including a code block and it gets messy in comments.
It seems like you're trying to preform a GET on /password instead of on /account/password.
From what I'm reading, you've got a mapping for /account/password:
devise_scope :user do # used to remove /users/ part from devise URLs
# ...
scope '/account' do # adds /account to URLs
get 'password' => 'devise/registartions#password', as: "change_password"
# this will match /account/passwordAnswer
do
end
So you should either remove the scope, or replace your get request in test with this:
get "/account/password", :user => #user
or this
get change_password_path(#user)
Where #user is a User's mock.
Run rake routes to confirm.
Have you set up your config/routes.rb with the following.
devise_for :users do
get 'logout' => 'devise/sessions#destroy'
get 'changepassword' => 'devise/registrations#edit'
get 'access' => 'homepages#access'
get 'history' => 'policies#history'
get 'future' => 'policies#future'
end
devise_for :users, :controllers => { :sessions => :sessions }
resources :users
I have the same problem. I set the routes then it was worked for me :)
I don't like using get "/account/password" or a path in my spec. Seems hacky. I fixed a similar problem by using better syntax in my routes.rb file:
Using path: 'account' option in devise_for
Explicitly setting my custom controller with controllers: {registrations: 'registrations'}
So my routes.rb looks like this:
devise_for :users,
path: 'account',
path_names: {sign_in: 'login', sign_out: 'logout', sign_up: 'register'},
controllers: {registrations: 'registrations'},
skip: [:passwords]
Then I can use get :new in my test. Much cleaner.
Hope this helps someone else.
I am using devise 3.0.
Related
I have a few links that are breaking. One, my logout, which I am using the delete method with, returns this error:
[Devise] Could not find devise mapping for path "/users/sign_out". This may happen for two reasons: 1) You forgot to wrap your route inside the scope block. For example: devise_scope :user do get "/some/route" => "some_devise_controller" end 2) You are testing a Devise controller bypassing the router. If so, you can explicitly tell Devise which mapping to use: #request.env["devise.mapping"] = Devise.mappings[:user]
I have this in my routes: get '/users/sign_out', to: 'devise/sessions#destroy'
And my devise routes look like this:
devise_for :users, controllers: { sessions: 'sessions',
registrations: 'registrations',
invitations: 'invitations' }
Why is this breaking?
I have this in my routes: get '/users/sign_out', to: 'devise/sessions#destroy'
if you want to allow the user to sign out via GET method all you have to do is go to app/config/initializers/devise.rb and uncomment the line config.sign_out_via = :get
OR Try this
devise_scope :user do
get '/users/sign_out', to: 'devise/sessions#destroy'
end
In my application all routes are scoped to a locale, that user has selected like this:
scope ":locale", locale: /#{SpreeI18n::Config.supported_locales.join('|')}/ do
devise_for :users, skip: :omniauth_callbacks
get '/', to: 'homepage#index', :as => :homepage
end
When I want to send reset password instructions like User.find(1).send_reset_password_instructions, there is a problem:
Devise::Mailer#reset_password_instructions: processed outbound mail in 4249.9ms
ActionView::Template::Error: No route matches {:action=>"edit", :controller=>"devise/passwords", :reset_password_token=>"-zyuNkscVkwFn2awdm27"} missing required keys: [:locale]
How can I pass locale so that I can send the reset token?
Let's create a custom controller for password:
Customize routes.rb
scope ":locale", locale: /#{SpreeI18n::Config.supported_locales.join('|')}/ do
devise_for :users, skip: :omniauth_callbacks, controllers: { passwords: 'my_passwords' }
get '/', to: 'homepage#index', :as => :homepage
end
my_passwords_controller.rb
class MyPasswordsController < Devise::PasswordsController
def create
resource_params.merge!(locale: 'en') # use 'en' for eg
super
end
end
Then send_reset_password_instructions function will take your customized resource_params when sending the email.
Please refer to devise sourcecode to understand what devise does in detail!
I'm receiving the following error when trying to go to http://app.mysite.dev/login -
Could not find devise mapping for path "/login".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
Now, here is the relevant bits of my routes.rb file:
namespace 'app', path: '', constraints: { subdomain: 'app' } do
devise_for :users, :skip => [:registrations, :confirmations]
devise_for :agents, :skip => :sessions
devise_scope :users do
get "login" => "users/sessions#new"
end
...
end
And the route generated by the get "login" line is as follows (from rake routes)
app_login GET /login(.:format) app/users/sessions#new {:subdomain=>"app"}
I don't know if it matters, but I'm using STI for Users > Agents relationship.
So, I already am defining the scope for devise, and I'm not testing, so any ideas what's going on?
Try to replace your devise_scope with the following instead. Within your namespace 'app' block.
devise_scope :app_user do
get "login" => "users/sessions#new"
end
It appears to be devise was changing the scope it was looking for within a namespace.
For your reference:
https://github.com/plataformatec/devise/issues/2496
And yeah, it should be devise_scope :app_user instead of devise_scope :app_users
It's just a simple typo - devise_scope :users should be devise_scope :user, as stated in the error message.
It seems you didn't define a custom SessionsControllerfor your :users, and Devise cannot use it's default one since you namespaced your devise_scope :users.
I'd define your own custom class App::SessionsController and then add it rewrite your routes like this:
namespace 'app', path: '', constraints: { subdomain: 'app' } do
devise_for :users, controllers: { sessions: 'sessions' }, skip: [:registrations, :confirmations]
devise_scope :users do
get "login" => "sessions#new"
end
end
I am trying to enable functional testing for custom Devise routes, but I keep hitting roadblocks that I am not sure how to solve. The routes I am trying to test are in the devise_scope :user block of my routes file (see below)
devise_for :users, :controllers => {:registrations => "registrations",
:confirmations => "confirmations",
:sessions => "sessions"}
devise_scope :user do
get '/trainer/:referral_trainer' => "registrations#new"
get '*referral_trainer' => "registrations#new_client"
post '/client_sign_up' => "registrations#create_client"
end
and my tests look like:
test "should get new" do
params = {}
params["referral_trainer"] = "trainer"
get("new", params)
assert_response :success
params = {}
params["trainer"] = "trainer"
get("new_client", params)
assert_response :success
end
But the error(s) I get are:
ActionController::RoutingError: No route matches {:referral_trainer=>"trainer", :controller=>"registrations", :action=>"/trainer/*referral_trainer"}
ActionController::RoutingError: No route matches {:trainer=>"trainer", :controller=>"registrations", :action=>"new_client"}
In my test I also have:
setup do
#controller = RegistrationsController.new
#request.env["devise.mapping"] = Devise.mappings[:user]
end
So that the right controller and Devise mappings are being used.
Rake routes returns:
GET /trainer/:referral_trainer(.:format) registrations#new
GET /*referral_trainer(.:format) registrations#new_client
So I know the routes exist (and have used them in production), but I can not figure out how to test them programmatically.
Any help would be appreciated.
Thanks!
I'm using Devise and everything has been working great, but I am now trying to move things into an 'admin' namespace.
I have a route that looks like:
namespace :admin do
devise_for :users, :controllers => { :registrations => "admin/users/registrations" }
end
In one of my controllers I have
before_filter :authenticate_user!
but when that gets called it throws:
ActionController::RoutingError (No route matches {:action=>"new", :controller=>"devise/sessions"}):
Any ideas?
As per the Devise documentation (which likely has changed since this posting), you could use the following instructions:
# ...
#
# Notice that whenever you use namespace in the router DSL, it automatically sets the module.
# So the following setup:
#
# namespace :publisher do
# devise_for :account
# end
#
# Will use publisher/sessions controller instead of devise/sessions controller. You can revert
# this by providing the :module option to devise_for.
#
# ...
Hope this helps someone.
I'm doing this:
scope '/admin' do
devise_for :admins
end
A workaround for this is to use the path option and move devise_for outside of the namespace block:
devise_for :users, :path => '/admin',
:controllers => { :registrations => "admin/users/registrations" }
namespace :admin do
# other resource controllers
end
Perhaps it's not as elegant (or intuitive), but it works for me!