TL;DR: Strip params
to only be what you want, I call this user_params
. In the action that processes the form check if user_params[:password]
is empty, and if it is try to update your @user
model with @user.update_without_password(user_params)
if it isn't try to update with @user.update(user_params)
. If the applicable update call returns false
@user.errors
holds the explanation.
Here's exactly how I solved this problem:
I defined a resource
in my config/routes
file:
resource :profile
I made a controller for my resource that extends devise's authenticated controller ProfilesController < AuthenticatedController
for managing profiles. Profiles controller contains several methods
including user_params
which mainly filters params
down to a whitelist:
def user_params
accessible = [
:first_name,
:last_name,
:email,
:password,
:password_confirmation
]
params.require(:user).permit(accessible)
end
and update
which does the business of processing the form:
def update
# current_user holds the logged in user
@user = current_user
# IF they've left the password field blank,
# AND the devise update_without_password method returns true
# OR IF a full update of user (including password and password_confirmation) returns true
# THEN re-sign them in to flush their session, and redirect them back to their dashboard, and send a success message.
# ELSE re-present the edit form they were just on (there's a handy catcher
# in the edit view script to render the form errors, you can find them on
# @user.errors)
if (user_params[:password].blank? && @user.update_without_password(user_params)) || @user.update(user_params)
sign_in(@user, bypass: true)
redirect_to '/dashboard', notice: 'Your profile changes have been saved.'
else
render 'edit'
end
end
There's also of course a view script (a haml one in my case - app/views/profiles/edit.html.haml
) that uses form_for
to render the form:
= form_for current_user, as: :user, url: profile_path, html: { class: '' } do |f|
# [f.label, f.text_field, f.password_field, etc...]
My user model also has all the devise-y goodness included:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :receipts
validate :first_name, presence: true
validate :last_name, presence: true
validate :email, presence: true, email: true
end