I am using a devise scope for my password reset route, and it is hitting the controller but not the actual method inside the controller. Mind boggling... Any help is appreciated.
devise_scope :dashboard_users do
post 'forgot_password', to: 'sessions#forgot_password'
put 'reset_password', to: 'sessions#reset_password'
end
That's in my routes.rb and then this is my sessions_controller
class Api::Dashboard::V1::SessionsController < Devise::SessionsController
p "hello"
def forgot_password
...
end
end
It will print the "hello" but wont print or execute any code inside the method.
hello
Processing by Api::Dashboard::V1::SessionsController#forgot_password as */*
Parameters: {"email"=>"jovannyadams#rmlabs.org"}
[Devise] Could not find devise mapping for path "/api/dashboard/v1/forgot_password".
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 don't know the exact answer to your question, but I have some ideas. First, the p "hello" is getting executed on file load, not on the request. It's probably confusing because Rails will automatically reload your controller on requests, printing the "hello" message. You can test this in an IRB console:
class Test
p "hello"
end
Second, I would take a look at your rake routes output. You may be sending requests to the wrong place. My hunch is that you need to adjust your to settings for the routes to something like:
post 'forgot_password', to: 'api/dashboard/v1/sessions#forgot_password'
put 'reset_password', to: 'api/dashboard/v1/sessions#reset_password'
That change will point the requests to your properly namespaced controller. The previous routes may have sent requests to a different sessions controller that was defined on the global namespace by either you or Devise.
Also, take a look at these posts from devise wiki pages:
https://github.com/plataformatec/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes
https://github.com/plataformatec/devise/wiki/How-To:-Customize-routes-to-user-registration-pages
Related
I'm using Devise along with CanCan/Rolify for authentication for my rails app and I'm having a hard time understanding how to pass Devise methods to namespaced controllers.
I'm using a standard users model and controller for the main part of my app, everything on that side works well and good. However, I have set it up that if an account with a specific role (which is determined after registration) logs in using the main login page, it will redirect them to namespaced views and controllers.
Lets say that user_id: 3 logs in and is redirected to 0.0.0.0/external/dashboard, I can call Devise's current_user method on the dashboard page after redirection and returns the correct information. However, when I set a link_to for that user to be able to edit his profile at 0.0.0.0/external/users/3/edit I receive the following error:
Started GET "/external/users/3/edit" for 127.0.0.1 at 2017-08-31 21:40:59 -0400
ActionController::RoutingError (undefined local variable or method `current_user' for External::UsersController:Class)
routes
devise_for :users, controllers: { confirmations: 'confirmations', registrations: 'registrations' }
# External routes
namespace :external do
resources :properties, :clients, :orders, :deeds, :mortgages, :users, :owners, :dashboard
end
external/dashboard view
<%= user_name_helper(current_user) %>
user_name_helper (produces user edit link)
def user_name_helper(user)
if user.name.blank?
link_to "Update your profile here.", edit_external_user_path(user)
else
"Hello, #{user.name}"
end
end
Error happens when I click the link produced by this helper.
But <%= link_to fa_icon("sign-out", text: "Logout"), destroy_user_session_path, method: :delete %> works fine from 0.0.0.0/external/dashboard.
So I am confused as to why I can call current_user within the view for the External::DashboardController but can't call it within the External::UsersController. All namespaced controllers inherit directly from ApplicationController and I want to keep only using a single users model. I've searched through a bunch of different articles but can't seem to find an answer with a configuration similar to mine, so can anyone explain to me what I might be doing wrong and how to get this to work as well as allowing me to have the ability to use any other Devise methods in my namespaced views? Thanks in advance.
Welp, I figured it out. The issue was that I was calling a conditional before_filter in my external/ controllers as follows:
before_filter :client_only unless current_user.has_role? :admin
Once I removed that everything worked as intended. I still have the following before_filter in place which works.
before_filter :client_only
...
private
def client_only
redirect_to root_path unless current_user.is_client?
end
Not entirely sure why the conditional filter wouldn't work the way I originally had it so if anyone has any insight and feels like sharing then I'm all ears, thanks.
In a Rails (version 4.0.2) have a simple model I generated with the Devise gem (version 3.0.4) called User. I want to customize the controller. So I made a file "user_controller":
class UserController < Devise::SessionsController
def create
super
puts "this works"
end
end
Here are my routes:
SecretApp::Application.routes.draw do
root 'static_pages#home'
match '/about', to: 'static_pages#about', via: 'get'
match '/profile', :to => "profile#show", via:'get'
devise_for :users, :controllers => {:registrations => "registrations", :sessions => "user"}
end
I'm assuming when overriding a Devise controller, you have to call super in each method unless you are completely overriding the method, correct? In any case, when I create a new User, the console never prints "this works", so I'm pretty sure the controller is not being used. Why?
I did however manage to override my registrations controller for the User model, which confuses me because I'm using the same method to override the User controller (besides the inherited super class and the controller name). I'm sure of this because I have puts "this is ok" in the the new method and it does print that to the console.
I agree with Zach. A controller that inherits from Devise::SessionsController should definitely not be called UserController. At most, that controller should be inheriting from Devise::RegistrationsController. Also, Rails' conventions dictate that you name your controller in the plural form, UsersController. Regardless, this is why your code is not working:
When you call super, the create action inside Devise::SessionsController is reached. Notice how, in the last line of that action, the respond_with method is called. This method is the one responsible for rendering a template, generating a response. Once this line is reached, your code will not run.
You have thus 2 options:
Call your custom code before you call super:
def create
puts "this works"
super
end
Or take advantage of the fact that Devise's create action has this line:
yield resource if block_given?
You can then call your custom code like this:
def create
super do |resource|
puts "this works"
end
end
This is all mentioned in Devise's readme.
In the second option, Devise is using two awesome Ruby features: blocks, and yielding. You can learn more about blocks and yield with RubyMonk, I highly recommend it!
I have a devise enabled route as:
#config/routes.rb
authenticated :user {
root :to => 'home#signed_in'
}
root :to => 'home#index
and controller:
#app/controllers/home_controller.rb
class HomeController < ApplicationController
def signed_in
Rails.application.routes.recognize_path '/'
end
end
which raises:
NoMethodError: undefined method `authenticate?' for nil:NilClass
...lib/devise/rails/routes.rb:286:in `block in authenticated'
I need such thing to render different templates in destroy action based on request.referer controller name. How can get 'authenticated' controller/action name for such URL?
It seems using Rails.application.routes.recognize_path does not work well with custom routes constraints like devise #authenticated method.
See https://github.com/plataformatec/devise/issues/3747
recognize_path won't work as is with constraints that require request related objects (like the warden instance that Devise expects to be in the request env Hash). If you are doing this inside a controller code you might be able to pass the warden object from request.env to the recognize_path call, or try to pass down the object down the the class that is recognizing the path.
In the other hand, as recognize_path isn't a documented public API, I strongly suggest you to not use it and save pieces of the params Hash instead of the raw URL on your application.
One solution to your problem could be to save the controller/action names in session and access them when you need.
authenticated :user do
root :to => 'home#signed_in'
end
unauthenticated :user do
root :to => 'home#index'
end
You can use this: https://gist.github.com/NullVoxPopuli/8c8af217b7404336c72a
class RouteRecognizer
include Singleton
ROUTE_LIST = Rails.application.routes.routes.collect{|r| r.path.spec.to_s}
REGEX_ROUT_LIST = ROUTE_LIST.map{|r|
Regexp.new(r.gsub(/\:(.*)id/, "(\d+)").gsub("(.:format)", ""))
}
def self.is_route?(path)
REGEX_ROUT_LIST.each do |regex|
return true if !!(path =~ regex)
end
false
end
end
and then call with
RouteRecognizer.is_route?('http://whatever')
This should be a simple one, but just could not find out why it caused the testing failure.
I kept getting the following error when running rspec. But after commenting "send" method, everything works fine.
1) Messages GET /messages works! (now write some real specs)
Failure/Error: get target_app_messages_path(#message.target_app.id)
ArgumentError:
wrong number of arguments (2 for 0)
# ./app/controllers/messages_controller.rb:37:in `send'
routes.rb
resources :target_apps do
resources :messages do
member do
post 'send'
end
end
end
Model Code
class Message
include Mongoid::Document
belongs_to :target_app
end
Controller Code
class MessagesController < ApplicationController
def index
...
end
def show
...
end
...
def send
...
end
end
/spec/requests/message_spec.rb
describe "Messages" do
describe "GET /messages" do
let(:message) do
FactoryGirl.create(:message)
end
it "works! (now write some real specs)" do
# Run the generator again with the --webrat flag if you want to use webrat methods/matchers
get target_app_messages_path(message.target_app.id)
response.status.should be(200)
end
end
Every object in Ruby has a send method:
http://ruby-doc.org/core-1.9.3/Object.html
By naming your action "send" you caused a name conflict. Try renaming that action to "sendmessage" or defining it in a different way. There should be a way to have "send" in your URL map to an action named "sendmessage".
A couple of things are wrong with this.
The first is what David Grayson pointed out. You can't call a method send.
The second, is how you've defined that action in your config/routes.rb, it should not be this:
resources :messages do
member 'send'
end
As that isn't defining an action at all. In fact, I'm not even sure what that does. It should be this (taking into account you can't call it send as well):
resources :messages do
member do
get 'deliver'
end
end
For more information, see the Routing guide.
The third thing you're doing wrong is that in your test, you need to reference the action for the controller and not use the route. This is because you're writing a functional controller test that tests a controller.
It should not be this:
get target_app_messages_path(message.target_app.id)
But rather, this:
get :deliver, :id => message.target_app.id
The method takes the action as the first argument, parameters as the second argument and session data as the third argument.
Trying to redirect users to their associated 'home' page after successful login w/out nil'ing out stored_location_for(resource_or_scope)...which is giving me some endless redirect loops (pretty sure I've set it up wrong).
Regardless, I'm looking for a better approach...
Devise's docs state: After
signing in a user, confirming the
account or updating the password,
Devise will look for a scoped root
path to redirect. Example: For a
:user resource, it will use
user_root_path if it exists,
otherwise default root_path will be
used. This means that you need to set
the root inside your routes: root :to => "home"
I'm sorta a newbie...how does one go about generating this home_root_path for each user?
rDocs also mention:
-- (Object) after_sign_in_path_for(resource_or_scope)
The default url to be used after
signing in. This is used by all Devise
controllers and you can overwrite it
in your ApplicationController to
provide a custom hook for a custom
resource.
By default, it first tries to find a resource_root_path, otherwise
it uses the root path. For a user
scope, you can define the default url
in the following way:
map.user_root '/users', :controller => 'users' # creates user_root_path
map.namespace :user do |user|
user.root :controller => 'users' # creates user_root_path
end
but these just gives me undefined local variable or methodmap' for #ActionDispatch::Routing::Mapper:…` errors.
If you would like to redirect using a route in answer to your question below:
how does one go about generating this home_root_path for each user?
This will work if you place it in your config/routes file. It will redirect a user to articles#index after (for example) a successful confirmation.
# Rails 4+
get 'user_root' => 'articles#index', as: :user_root
# Rails 3
match 'user_root' => 'articles#index', as: :user_root
See Devise: Controllers Filters and Helpers
You could try something like this:
application_controller.rb:
def after_sign_in_path_for(resource_or_scope)
# return home_page_path for user using current_user method
end
Dug around a bit to figure out the same thing. #polarblau's answer is correct,
def after_sign_in_path_for(resource_or_scope)
user_info_path(current_user)
end
where user_info_path is the path to the page you wish to display.
Also, I would allow this to fall back to super just in case, although I'm not entirely sure if that is necessary...
def after_sign_in_path_for(resource)
if resource.is_a(User)
user_info_path(resource)
else
super
end
end
I spent several hours trying to get the same functionality, and this is the code that ended up working for me:
def after_sign_in_path_for(resource)
current_user
end
If I ever tried current_user_path, I always got undefined local variable or method current_user_path errors.
Also, I'm using Rails 3.2.8 and Devise 2.1.2.
Hope that helps.
Based on #SnapShot answer, this worked for me. I'm using multiple devise models, trying to redirect back to the users profile edit page.
get 'user_root', to: redirect('/users/edit'), as: :user_root
ROR 7 answer
get '/users/home' => 'application#test', as: :user_root