2

I have a User model with name and password attributes. When the user clicks on 'Edit' I want just the name field to be editable and validated but not the password field. And vice versa for when the user clicks on 'Reset Password'. I think my main problem is how do I turn password validation off during the name field edit and how do I turn off name validation during the password edit?

edit.html.erb

<%= form_for @user, html: { class: "form_settings" } do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <p><span><%= f.label :name %></span>
    <%= f.text_field :name %>
  </p>

  <p style="padding-top: 15px"><span>&nbsp;</span>
    <%= f.submit "Submit", class: "submit" %>
  </p>
<% end %>

reset_password.html.erb

<%= form_for @user, html: { class: "form_settings" } do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <p><span><%= label_tag :old_password, "Current Password" %></span>
    <%= password_field_tag :old_password %>
  </p>

  <p><span><%= f.label :password %></span>
    <%= f.password_field :password %>
  </p>

  <p><span><%= f.label :password_confirmation %></span>
    <%= f.password_field :password_confirmation %>
  </p>

  <p style="padding-top: 15px"><span>&nbsp;</span>
    <%= f.submit "Submit", class: "submit" %>
  </p>
<% end %>

users_controller.rb

def update
  @user = User.find(params[:id])
  if params[:old_password]
    if @user.authenticate(params[:old_password])
      @user.update_attributes(password: params[:user][:password])
      flash[:success] = "Password has been updated"
      redirect_to @user
    else
      flash.now[:error] = "Current password is incorrect"
      render :reset_password
    end
  elsif @user.update_attributes(params[:user])
    flash[:success] = "User name updated"
    redirect_to @user
  else
    render :edit
  end
end

My other problem is that password and password_confirmation validation is not working. This is what I have in the model:

validates :password, presence: true, confirmation: true

UPDATE Got the password to reset and the validation working with this code:

def update
  @user = User.find(params[:id])
  if params[:old_password]
    if @user.authenticate(params[:old_password])
      if params[:user][:password] == params[:user][:password_confirmation]
        @user.update_attributes(password: params[:user][:password])
        flash[:success] = "Password has been updated"
        redirect_to @user
      else
        flash.now[:error] = "Passwords don't match"
        render :reset_password
      end
    else
      flash.now[:error] = "Current password is incorrect"
      render :reset_password
    end
  elsif @user.update_attributes(params[:user])
    flash[:success] = "User name updated"
    redirect_to @user
  else
    render :edit
  end
end

But it seems too complicated for me. Anyone see an easier solution?

But I still have the problem of editing the name field. It's saying password can't be blank.

Mike Glaz
  • 5,352
  • 8
  • 46
  • 73

3 Answers3

1

Move validation logic to model and keep the controller clean.

validates :password, presence: true, confirmation: true, if: password?, on: :update
validates :name, presence: true, if: name?, on: :update

This is Required when u create the record. Just add on: :create

validates :password, presence: true, confirmation: true, on: :create
validates :name, presence: true, on: :create

And modify your controller like this,

def update
  @user = User.find(params[:id])
  @user.authenticate(params[:old_password]) if params[:old_password]
  return redirect_to @user, notice: "Sucessfully updated" if @user.update_attributes(params[:user])
  render Rails.application.routes.recognize_path(request.referer)[:action]
end

Used render idea from this link: Render the action that initiated update

Community
  • 1
  • 1
  • I'm sorry, I don't understnad what if: password? and if: name? do. Are they methods I need to build? – Mike Glaz Mar 06 '13 at 01:53
  • Hmmm. They are rails methods, if password has some value it will return true otherwise false, same holds for name as well. When u update through reset password The First Validation will Run, When U update the name only at that case the second validaion will run. – Abibullah Rahamathulah Mar 06 '13 at 11:55
  • I think it needs to be with a colon :password? – Mike Glaz Mar 06 '13 at 22:35
1

You should not mix the name edit and the password reset on the same action.

Create one action that will update the attributes of your user, the password field should not be white listed with attr_accessible

Create one other action that will deal with the password reset.

def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    flash[:success] = "User name updated"
    redirect_to @user
  else
    render :edit
  end
end

def reset_password
  if params[:old_password] && @user.authenticate(params[:old_password])
    if params[:user][:password] == params[:user][:password_confirmation]
      @user.password = params[:user][:password]
      flash[:success] = "Password has been updated"
      redirect_to @user
    else
      flash.now[:error] = "Passwords don't match"
      render :reset_password
    end
  else
    flash.now[:error] = "Current password is incorrect"
    render :reset_password
  end
end

This way, each action has its semantic role.

Btw, I hope you're using something like bcrypt to store the password.

Intrepidd
  • 19,772
  • 6
  • 55
  • 63
  • For some reason the update action gets called when I try to reset the password like this. – Mike Glaz Mar 03 '13 at 18:30
  • Do you use a form ? You should point each button to a different action – Intrepidd Mar 03 '13 at 18:32
  • Yes, that's it. Did not know you can put an action into a form_for. I'm finally at a point where I'm understanding fellow Rails programmers' answers. Thanks – Mike Glaz Mar 03 '13 at 19:54
0

Figured it out...but it looks kinda ugly:

def update
  @user = User.find(params[:id])
  if params[:old_password]
    if @user.authenticate(params[:old_password])
      if params[:user][:password] == params[:user][:password_confirmation]
        @user.update_attributes(password: params[:user][:password])
        flash[:success] = "Password has been updated"
        redirect_to @user
      else
        flash.now[:error] = "Passwords don't match"
        render :reset_password
      end
    else
      flash.now[:error] = "Current password is incorrect"
      render :reset_password
    end
  elsif params[:user][:name].blank?
    flash[:error] = "Name can't be blank"
    render :edit
  else
    @user.update_attribute(:name, params[:user][:name])
    flash[:success] = "User name updated"
    redirect_to @user
  end
end
Mike Glaz
  • 5,352
  • 8
  • 46
  • 73