Can't resolve "Current password can't be blank" when editing user

4,708 views
Skip to first unread message

Mark Richman

unread,
Nov 27, 2011, 10:49:42 AM11/27/11
to plataforma...@googlegroups.com

This HOWTO does not work for me: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password

I just did a git clone git://github.com/RailsApps/rails3-devise-rspec-cucumber.git and followed the instructions for Solution 1. I removed the :current_password field from /views/devise/registrations/edit.html.erb and added an update method to my UsersController as follows:


class UsersController < ApplicationController
  before_filter :authenticate_user!

  def show
    @user = User.find(params[:id])
  end

  def update
      logger.info "UsersController#update"
          
      # Override Devise to use update_attributes instead of update_with_password.
      # This is the only change we make.
      if resource.update_attributes(params[resource_name])
        set_flash_message :notice, :updated
        # Line below required if using Devise >= 1.2.0
        sign_in resource_name, resource, :bypass => true
        redirect_to after_update_path_for(resource)
      else
        clean_up_passwords(resource)
        render_with_scope :edit
      end
    end

end

I still get prompted with the validation error "Current password can't be blank".

Per Jose's suggestion, I also tried calling super in UsersController#update with no effect.


Thanks,

Mark


Carlos Antonio da Silva

unread,
Dec 3, 2011, 1:02:47 PM12/3/11
to plataforma...@googlegroups.com
There is no super to call in this situation, you're just using a bare controller (and no Devise related code). You just can't give the :password and :password_confirmation keys (even blank), because Devise requires both to be nil, otherwise it'll try to validate (of course, only if you're using validatable).
--
At.
Carlos Antonio

Hassan Shahid

unread,
Jul 25, 2012, 9:25:23 AM7/25/12
to plataforma...@googlegroups.com
This issue is slightly outdated, but I thought I'd join and add a response in case someone else comes around trying to do this, since I just had to deal with this. 

The issue with the HOWTO you referenced is two-fold.  First, it makes no correlation between devise and your app.  You have to override the Devise Registrations controller, or you essentially are building your own auth system.

So the first thing to recognize is the method and controller that is being called which prevents you from updating your profile without a password.  This is Devise::RegistrationsController#update.  Now that we have that, we can do a step-by-step on getting to the end result.

1.  generate a new controller.  Call it whatever you want, I prefer to stick with registrations

rails generate controller registrations

2.  in your config/routes.rb file, point the users route to call the registrations controller for all things registration.

devise_for :users, :controllers => {:registrations => 'registrations'} #the right assignment is whatever you called your ###controller

3.  call rake routes.  You should see all the routes to the registration controller, and all the old devise registration routes will not appear:  The routes should look like:

cancel_user_registration  GET /users/cancel(.:format)          registrations#cancel
user_registration         POST /users(.:format)                registrations#create
new_user_registration     GET /users/sign_up(.:format)         registrations#new
edit_user_registration    GET /users/edit(.:format)            registrations#edit
                          PUT /users(.:format)                 registrations#update
                          DELETE /users(.:format)              registrations#destroy

4.  open your registrations_controller.rb file.  This is a really two steps:
  a.  Change the inheritance from ApplicationsController to Devise::RegistrationsController
 b.  For each method listed above in step 3 (registrations#method_name) with the exception of 'update', create a method and just call super:

class RegistrationsController < Devise::RegistrationsController

  def new
    super
 end 
 
 def create
    super
 end 
 
 def edit
    super
 end 
 
 def cancel
    super
 end 

 def destroy
    super
 end 
end

5.  Now that you have every method working just like it was except for update, all that's left to do is use the code from the how-to(https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-passwordin the registrations_controller.rb file, inside a method called 'update'.  Also, be sure to add the helpers from the how-to.  You can put them in a specific registrations_helper.rb file (or whatever you called your controller), or in your application_helper.rb file.  The one 'gotcha' in the example is it is looking to evaluate params[:user][:email].empty? and params[:user][:password].empty?.  I imagine many profile update pages will not necessarily consist of these fields, so this will throw a "nomethoderror on nil::nil class" error.  It's simply because those parameters don't exist if they're not in your form.  There are many ways of dealing with that, but just be aware of that simple oversight.

Hope this helps someone.

-Hassan  

Walter Lee Davis

unread,
Jul 25, 2012, 12:30:38 PM7/25/12
to plataforma...@googlegroups.com
This is a general code style question to follow up on this excellent how-to.

Is there any benefit in overriding the entire controller, only to call super in most of the methods inside it, rather than to route only the update calls to the override controller, and to only have that controller have an update method?

Is this a case where clarity of intent should go in the controller vs. the routes file?

Walter
> --
>
>
>

Hassan Shahid

unread,
Jul 25, 2012, 3:17:01 PM7/25/12
to plataforma...@googlegroups.com
Well, in this particular case the update method needed to be overridden, but I can certainly see scenarios where others might need to be.  It almost seems like there's a benefit in being able to generate a local version of any devise controller, much like how you can generate the devise views locally to customize.  That gives the advantage of not trying to building all the edge cases into devise, and giving an easier and best-practice mechanism to do customizations as needed.


--




Walter Lee Davis

unread,
Jul 25, 2012, 3:25:19 PM7/25/12
to plataforma...@googlegroups.com

On Jul 25, 2012, at 3:17 PM, Hassan Shahid wrote:

> Well, in this particular case the update method needed to be overridden, but I can certainly see scenarios where others might need to be. It almost seems like there's a benefit in being able to generate a local version of any devise controller, much like how you can generate the devise views locally to customize. That gives the advantage of not trying to building all the edge cases into devise, and giving an easier and best-practice mechanism to do customizations as needed.

I understand what you are saying there, the question is one of style -- should the override be surgical, or all-in-one, when you are only overriding one method? I have seen some examples where this technique was used in routes.rb:

devise_scope :user do
match 'users/invitation/new_practice', :to => 'invitations#create_practice'
#…
end

which meant that the invitations_controller I wrote (which extended the gem-based version) was just that method and another override, everything else was picked up from the parent because it wasn't declared in the child.

Walter
> --
>
>
>

Hassan Shahid

unread,
Jul 25, 2012, 3:32:59 PM7/25/12
to plataforma...@googlegroups.com
Ah, I gotcha.  Yeah that's interesting.  My personal preference is the routes file to be extremely clean, just because I think it's more confusing to see a routes file that is routing one action one place, and everything else somewhere else.  Looking at the controller and seeing its inheritance is really clear, and it makes for only one place that I have to edit if I do need to customize.  The alternative requires an edit in both the routes file and controller file.

--




Hassan Shahid

unread,
Jul 26, 2012, 7:02:46 AM7/26/12
to plataforma...@googlegroups.com
You know, I realized shortly after having this conversation, that you don't need to define the methods that are you aren't overriding at all. The fact that you're inheriting from the Devise::RegistrationsController, if it doesn't find the method in your local controller, it moves up the chain and finds it in there. Silly me. So, in reality, all you do is override the method(s) you want, and you're good to go.

-Hassan
> --
>
>
>

Reply all
Reply to author
Forward
0 new messages