-
Notifications
You must be signed in to change notification settings - Fork 9
Description
My app is using message_block to show all errors and flash messages.
So I've got this in my application layout:
= message_block on: :all
It's been working great... with one exception. When I try to update my password while being logged in, if there is a validation error, the error output appears twice:
- Password is too short (minimum is 3 characters)
- Password confirmation doesn't match Password
- Password confirmation can't be blank
- Password is too short (minimum is 3 characters)
- Password confirmation doesn't match Password
- Password confirmation can't be blank
It took me a long time to figure out why this was happening. But I finally did.
When I try to change my password while I'm logged in, there are actually two instances of the User model loaded by the view.
One is an instance of current_user (the convenience method used by most Rails authentication gems as a proxy for a logged in user). The other is a more generic instance of the User model -- accessed via @user -- that I'm using to update the password.
And, yes, @user is really a copy of current_user, but there are still technically two User objects being loaded in the view.
Here are the relevant bits of the controller for reference
class PasswordsController < ApplicationController
before_action :require_login
def update
@user = current_user
if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
flash.now[:error] = "You forgot to enter a new password."
render :edit
return
end
@user.password_confirmation = params[:user][:password_confirmation]
if @user.change_password!(params[:user][:password])
@user.update_attribute(:reset_password_email_sent_at, nil)
flash[:notice] = "Your password was successfully updated."
redirect_to root_path
else
render :edit
end
end
end
One way to fix this would be to reject the current_user object from the list of objects that are checked for errors in the message_block helper.
model_objects = options[:on].map do |model_object|
if model_object == :all
assigns.delete("current_user") # <- ADD THIS LINE OF CODE
assigns.values.select {|o| o.respond_to?(:errors) && o.errors.is_a?(ActiveModel::Errors) }
elsif model_object.instance_of?(String) or model_object.instance_of?(Symbol)
instance_variable_get("@#{model_object}")
else
model_object
end
end.flatten.select {|m| !m.nil? }
But, ultimately, I think a better approach for the :all option would be to grab every user model from the app/models directory -- which is how we're doing it with Graffletopia, albeit as an initializer.
MODELS = Dir['app/models/*.rb'].map {|f| File.basename(f, '.rb').downcase.to_sym}
Thoughts?