lib/active_model/credit_card_number_validator.rb
# == ActiveModel Validations CreditCardNumberValidator
# Can be used in combination with the +validates+ method
#
# Only Amex and Maestro
#
# class CreditCard
# attr_accessor :number
# include ActiveModel::Validations
# validates :number, credit_card_number: {only: [:amex, :maestro]}
# end
#
# All numbers are valid except Maestro
#
# class CreditCard
# attr_accessor :number
# include ActiveModel::Validations
# validates :number, credit_card_number: {except: [:maestro]}
# end
#
# Proc can be used as well
#
# class CreditCard
# attr_accessor :number, :card_type
# include ActiveModel::Validations
# validates :number, credit_card_number: {brands: ->{|record| Array(record.accepted_brands) } }
#
# def accepted_brands
# if card_type == 'Maestro'
# :maestro
# elsif card_type == 'American Express'
# :amex
# else
# :visa
# end
# end
#
# end
#
#
module ActiveModel
module Validations
class CreditCardNumberValidator < EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, options[:message] || :invalid) unless credit_card_valid?(value, extract_brands(record, options))
end
def credit_card_valid?(number, brands = [])
CreditCardValidations::Detector.new(number).valid?(*brands)
end
protected
def extract_brands(record, options)
if options.has_key?(:brands)
with_brands(record, options[:brands])
elsif options.has_key?(:only)
Array(options[:only])
elsif options.has_key?(:except)
Array(CreditCardValidations::Detector.brands.keys) - Array(options[:except])
else
[]
end
end
def with_brands(record, brands)
if brands.is_a?(Proc)
brands.call(record)
elsif options[:brands] == :any
[]
else
Array(options[:brands])
end
end
end
end
end