johnrees
johnrees

Reputation: 327

Validating presence of has_secure_password only when password is in params

I have 4 forms associated with the User model, this is how I'd like them to behave -

  1. Registration (new) - Password REQUIRED - email, name, password, password_confirmation
  2. Edit Settings (edit) - Password NOT REQUIRED - email, name
  3. Change Password - Password REQUIRED, page for logged in users to change their password - current_password, password, password_confirmation
  4. Reset Password - Password REQUIRED, page for unauthenticated users to change their password, after arriving from a password reset email - password, password_confirmation

The closest validation solution I have is -

validates :password, presence: true, length: { minimum: 6 }, on: :update, if: lambda{ |u| u.password.blank? }

This will satisfy points 1, 3 and 4 but it will not allow a user to update attributes i.e. def update (point 2) because there is no params[:user][:password], so the {presence: true} validation is triggered.

What's the best way to validate_presence_of :password, only when the form contained a password field?

Upvotes: 3

Views: 2425

Answers (4)

Kuldeep
Kuldeep

Reputation: 884

Just try this:

validates :password, presence: true, length: { minimum: 6 }, allow_nil: true

it will validate password if and only if password field will be present on the form.

But this is dangerous, if somebody delete password field from the html then this validation won't trigger because then you will not get params of password.So you should do something like this:

validates :password, presence: true, length: { minimum: 6 }, if: ->(record) { record.new_record? || record.password.present? || record.password_confirmation.present?  }

Upvotes: 4

Hemali
Hemali

Reputation: 465

You can use devise gem or else see its implementaion. here is the source code https://github.com/plataformatec/devise

Upvotes: 0

hangsu
hangsu

Reputation: 507

To answer your question, It seems best to have change_password and reset_password controller actions and do your validations in there.

First, have your model validate password only on create or when it's already entered as such:

validates :password, :presence => true,
                     :length => { minimum: 6 },
                     :if => lambda{ new_record? || !password.nil? }

Then for your new controller actions, something along the lines of:

class UsersController < ApplicationController

  def change_password
    ...
    if params[:user][:password].present?
      @user.update_attributes(params[:user])
      redirect_to root_path, notice: "Success!"
    else
      flash.now.alert "Please enter password!"
      render "change_password"
    end
  end
end

Do the same for reset_password and you should be golden!

Upvotes: 1

NM Pennypacker
NM Pennypacker

Reputation: 6932

You could do something like this:

validates: validator_method


def validator_method(params_input)
if params.empty?
...
else
your_logic_her
end

You'll have to forgive me for using psuedo-code, but the idea is that you create a separate method to validate instead of using validates:

Upvotes: 0

Related Questions