Reputation: 9477
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
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
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
Reputation: 14983
You could check the errors
array and return.
def check_valid_bank_account
return unless errors.blank?
…
end
Upvotes: 35