Issues with overriding Devise controllers - ruby-on-rails

I'm trying to override a Devise controller to have some minor changes, for example adding a flash message when requesting a confirmation email for an unregistered email address.
I tried to override Devise::ConfirmationsController1 this way:
# app/controllers/confirmations_controller.rb
class ConfirmationsController < Devise::ConfirmationsController
include Devise::Controllers::InternalHelpers # tried to add this, no success
def create
self.resource = resource_class.send_confirmation_instructions(params[resource_name])
if successfully_sent?(resource)
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
end
I think I added the route correctly:
devise_for :users, :controllers => { :confirmations => "confirmations" }
My controller method gets called, however it raises this exception:
NoMethodError in ConfirmationsController#create
undefined method `successfully_sent?' for #<ConfirmationsController:0x007fa49e229030>
In my overridden controller, I just copied the code of Devise:: ConfirmationsController#create, which itself calls successfully_sent?(resource)
The successfully_sent? method is defined in InternalHelpers 2, this is why I tried to add include Devise::Controllers::InternalHelpers
This is not the first time I try to override a Devise controller, and this is not the first time I fail. I always managed to get a workaround, but I'd like to understand what I'm missing... Thanks in advance for your help!
[EDIT]
Devise is in version 1.4.9
Rails is 3.0.10

Well, thanks to the help of Kyle in my question's comments, I will write the correct answer to this beginner's mistake.
Instead of looking at my own version of Devise to override the controller, I was simply looking at Devise's Github repository. Since the controller I was trying to override had changes between my version and the last committed one, the helper method I was trying to use was simply not defined in my version...
As indicated by Kyle, you can use bundle open devise to look at the code of the gem you're actually using, or you can look at its version number with gem list devise and find the code for this release on Github (for Devise they set the tags for each release so that you can browse the code for release 1.4.9 by selecting the corresponding tag).
Doing this, I would have overrode my controller's create method with the following code instead:
def create
self.resource = resource_class.send_confirmation_instructions(params[resource_name])
if successful_and_sane?(resource)
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
else
respond_with_navigational(resource){ render_with_scope :new }
end
end
which uses successful_and_sane? and not successfully_sent? ...
To conclude this answer, there may be a better way of adding a flash message to this method than overriding it. jarrad is advising to use around_filter, but I can't get it to work yet and I'm unsure I can still change the rendered view after I yielded it from the filter method... Comments welcomed!

This may not help you understand why overriding the Devise controller is failing, but it will keep your code DRY in that you do not need to copy the code from Devise::ConfirmationsController#crete
So, if you just want to set a flash message, look at Filters for ActionControllers
Specifically, look at the Around Filter:
class ConfirmationsController < Devise::ConfirmationsController
around_filter :my_custom_stuff, :only => :create
private
def my_custom_stuff
# do your thing here...
end
end

Related

Devise redirect path after sign-up and before confirmation

I have implemented the devise gem on my app and I want to redirect to a specific path after sign_up. I have this method in the registration_controller.rb but its does not work, it redirects to the root path instead of the specified path. I also have devise :confirmable set up but I'm delaying the sent confirmation email until later.
def after_sign_up_path_for(resource)
new_transaction_path(session[:registration_params])
end
And returns the following flash notice: translation missing: en.devise.registrations.store.signed_up_but
How can I make this work?
You must be over riding the method in another file. Try searching project wide for after_sign_in_path_for. I have the following in my application controller working perfectly:
class ApplicationController < ActionController::Base
def after_sign_in_path_for(*)
cms_path
end
end
Where cms_path is a named route in my routes file. If yours isn't working you must be over riding it somewhere else.
I got this to work by adding the following on the registration_controller.rb
def after_inactive_sign_up_path_for(resource)
new_transaction_path(session[:registration_params])
end

Rails Devise Invitable redirect after send invititaion

I got the devise invitable installed and working. Trying to figure out how to redirect the user after he/she sent an invitation out. Right now it's redirecting me to the root. I thought you can just set your custom path in the method below but it didn't work. Thanks in advance if anyone know where to customize the path after invite sent.
def after_invite_path_for(resource)
new_profile_path
end
I stumbled upon your question because I was having the same issue. As far as I can tell the intended way for you to override after_invite_path_for is to override Devise::InvitationsController.
class Users::InvitationsController < Devise::InvitationsController
def after_invite_path_for(resource)
new_profile_path
end
end
routes.rb
devise_for :users, :controllers => { :invitations => "users/invitations" }
It would be nice if devise invitable worked like devise proper and you could override its after invite/accept paths in application controller. I modified devise_invitable to work that way and submitted a pull request. I'm not sure if it will be accepted or not, but you can have a look here: https://github.com/scambra/devise_invitable/pull/240.
If that feature is accepted, you could patch your current version of invitable to respect definitions of after invite/accept paths in application controller by putting this in an initializer:
#make invitable's path functions overridable in application controller
[:after_invite_path_for, :after_accept_path_for].each do |method|
Devise::InvitationsController.send(:remove_method, method) if ApplicationController.method_defined? method
end
Not sure if its a good thing... or the worse to do however you can put:
def after_invite_path_for(resource)
new_profile_path
end
in your application controller... seems to work OK!

About overriding Devise or Clearance controllers

Since authentication gems such as Devise or Clearance uses their own built in controllers, I have a few questions when overriding them. Everytime I've tried to override it, something seems to go wrong and I don't know what it is exactly that caused the error.
For example, to create a new user controller with Devise I understand I have to create a controller like this:
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
All good. Now let's say I want to add certain things to the def new parts of the controller.
1.) To leave the def create part of the controller alone, I have to put in
def create
super
end
Is that right? Or do I even need to reference it in the new controller at all?
2.) If I type
def new
#my custom code here
end
Does that replace the def new part of the original Devise controller, or does it just add to it? Meaning to say, do I also have to put in
resource = build_resource({})
respond_with_navigational(resource){ render_with_scope :new }
which is the default behavior for the def new part of the Devise registrations_controller.rb?
3.) There's a filter in Devise that prevents you from signing up if you're logged in, but I need to override this. How do I do this? I'm guessing it has something to do with the prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ] part of registrations_controller.rb, but I'm not too sure.
The same questions apply to Clearance, although with slightly different routes and files.. (I'm asking for Clearance too because I haven't decided which authentication gem to use yet -- Clearance appeals to me because of the lightweight code, but Devise has additional features that I would need too).
1) That's correct.
2) If you want to call the parent's logic, you can call super at the appropriate point in your sub-class logic.
3) If you override the RegistrationsController, you can call skip_before_filter :require_no_authentication. This should skip it entirely, so if you need the before filter in certain conditions, you would have to add another before_filter.

Devise gem in Rails: generate user_root_path

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

Custom Devise controller

I would like to customize my registrations controller for Devise in Rails. I understand that you must create a controller like this:
class AccountsController < Devise::SessionsController
def create
super
end
end
Well, that's all very good. But then let's say I want to fully control what happens in my #create action. How do I do that? How do I manually create a model and pass it all the params? Would Account.create(params[:account]) handle it smoothly? Is there some internal stuff going on I should know about or is my only option to call #super inside the action?
As long as you fulfil your required fields you can call Account.create in your example, I'm pretty sure the default Devise required fields are login, password and password_confirmation
We do this in a CRUD screen for creating devise users,
#admin = Admin.new(params[:admin])
if #admin.save
redirect_to admin_admins_path, :notice => 'New Administrator has been added'
else
render :action => "new"
end
and you don't want to extend the Devise session controller, a normal controller extending ApplicationController is fine or you can extend Devise::RegistrationsController and overwrite the methods you want to tweak in a registrations_controller.rb file
You can also have a look at the source on Github, if you want to be sure you're overriding things properly, and be sure you're not missing any processing...
https://github.com/plataformatec/devise/tree/master/app/controllers

Resources