aaronrussell
aaronrussell

Reputation: 9477

perform one validation only if all other validations pass

I am building a custom validation that checks a bank account number and sort code with an external API to test if they exist (i.e. is a proper valid UK bank account). As this is an expensive operation I don't want to bother hitting the API unless the account number and sort code pass Rails' built in validations.

For example, I have these basic validations:

validates_presence_of :sort_code, :account_number
validates_format_of :sort_code, :with => Regexes::SORT_CODE
validates_format_of :account_number, :with => Regexes::ACCOUNT_NUMBER

Then I have my custom validation:

validate :check_valid_bank_account

def check_valid_bank_account
  # code here is irrelevant, but essentially this hits the API
  # if it's a valid UK bank account all is OK, if not we add an error
end

What I want to ensure is that the custom validation is only performed if the rest of the model is valid. No point paying 25p to be told no account number was provided when I can work that out myself!

I am aware I could write some logic that checks that the two attributes are not blank and matches them against the regex manually... but that doesn't seem a very Rails way.

Upvotes: 30

Views: 12622

Answers (3)

Orlando
Orlando

Reputation: 9712

use something like this

validates_presence_of :sort_code, :account_number
validates_format_of :sort_code, :with => Regexes::SORT_CODE
validates_format_of :account_number, :with => Regexes::ACCOUNT_NUMBER
validate :check_valid_bank_account, :if => :should_i_call_custom_validation?

def should_i_call_custom_validation?
  # check for attributes or errors, errors.empty? should work
end

also a Proc should work here too

validate :check_valid_bank_account, :if => Proc.new{|object| object.errors.empty?}

Upvotes: 6

Pan Thomakos
Pan Thomakos

Reputation: 34350

I would actually recommend taking this code out of a validation method and putting it into a separate "valid_bank_account?" method that you can call manually when you actually want to hit the API, especially because it's an expensive operation. Some reasons for this behavior are that you might not want to run this validation if the account number has not changed or if you are only updating the record.

def save_only_with_valid_bank_account
  if @account.valid? && @account.valid_bank_number? && @account.save
   ...
  end
end

It's more tedious but it ensures that you have control over when the validation actually occurs.

Upvotes: 9

edgerunner
edgerunner

Reputation: 14983

You could check the errors array and return.

def check_valid_bank_account
  return unless errors.blank?
  …
end

Upvotes: 35

Related Questions