Romain Champourlier
Romain Champourlier

Reputation: 2360

Issues with overriding Devise controllers

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

Upvotes: 1

Views: 3053

Answers (2)

Romain Champourlier
Romain Champourlier

Reputation: 2360

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!

Upvotes: 2

jarrad
jarrad

Reputation: 3292

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

Upvotes: 1

Related Questions