Skip to content

Instantly share code, notes, and snippets.

@kyledcline
Last active December 22, 2023 14:43
Show Gist options
  • Save kyledcline/0bdcb3b198b85d347744661ded50b16e to your computer and use it in GitHub Desktop.
Save kyledcline/0bdcb3b198b85d347744661ded50b16e to your computer and use it in GitHub Desktop.
ABA Routing Number Validator for Rails
# frozen_string_literal: true
class RoutingNumberValidator < ActiveModel::EachValidator
WEIGHTS = ([3, 7, 1] * 3).freeze
DIVISOR = 10
def validate_each(record, attribute, value)
return if value.blank?
@record = record
@attribute = attribute
@value = value
# TODO: Canada routing number validation missing
# See https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/check.rb
case options[:format]
when :aba
validate_numericality
validate_length(9)
validate_checksum
else
raise ArgumentError, "#{self.class} does not support format '#{options[:format]}'"
end
end
private
def validate_numericality
ActiveModel::Validations::NumericalityValidator.new(
attributes: @attribute,
only_integer: true
).validate(@record)
end
def validate_length(length)
ActiveModel::Validations::LengthValidator.new(
attributes: @attribute,
is: length
).validate(@record)
end
def validate_checksum
return if checksum.zero?
@record.errors.add(@attribute, :invalid_routing_number, **options)
end
def checksum
return 1 if @record.errors[@attribute].any?
@value.chars.map(&:to_i).zip(WEIGHTS).sum { |digit, weight| digit * weight } % DIVISOR
end
end
@kyledcline
Copy link
Author

kyledcline commented Dec 22, 2023

Usage

validates :routing_number, routing_number: { format: :aba }, presence: true
  • Attribute must be type String.
  • presence option not required.
  • Define in config/locales/errors.en.yml or similar:
  en:
    errors:
      messages:
        invalid_routing_number: "My custom error message here"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment