module ActiveModel # :nodoc:
module Validations # :nodoc:
class StrengthValidator < EachValidator # :nodoc: all
def initialize(options)
super(options.reverse_merge(:level => :good, :with => :username, :using => PasswordStrength::Base))
end
def validate_each(record, attribute, value)
return unless PasswordStrength.enabled
strength = options[:using].new(record.send(options[:with]), value,
:exclude => options[:exclude],
:record => record
)
strength.test
record.errors.add(attribute, :too_weak, options) unless PasswordStrength.enabled && strength.valid?(level(record))
end
def check_validity!
raise ArgumentError, "The :with option must be supplied" unless options.include?(:with)
raise ArgumentError, "The :exclude options must be an array of strings or a regular expression" if options[:exclude] && !options[:exclude].kind_of?(Array) && !options[:exclude].kind_of?(Regexp)
check_level_validity!(options[:level])
super
end
def level(record)
if options[:level].respond_to?(:call)
level = options[:level].call(record).to_sym
check_level_validity!(level)
level
else
options[:level]
end
end
private
def check_level_validity!(level)
unless [:weak, :good, :strong].include?(level) || level.respond_to?(:call)
raise ArgumentError, "The :level option must be one of [:weak, :good, :strong], a proc or a lambda"
end
end
end
module ClassMethods
# Validates that the specified attributes are not weak (according to several rules).
#
# class Person < ActiveRecord::Base
# validates_strength_of :password
# end
#
# The default options are :level => :good, :with => :username.
#
# If you want to compare your password against other field, you have to set the :with option.
#
# validates_strength_of :password, :with => :email
#
# The available levels are: :weak, :good and :strong
#
# You can also provide a custom class/module that will test that password.
#
# validates_strength_of :password, :using => CustomPasswordTester
#
# Your +CustomPasswordTester+ class should override the default implementation. In practice, you're
# going to override only the +test+ method that must call one of the following methods:
# invalid!, weak!, good! or strong!.
#
# class CustomPasswordTester < PasswordStrength::Base
# def test
# if password != "mypass"
# invalid!
# else
# strong!
# end
# end
# end
#
# The tester above will accept only +mypass+ as password.
#
# PasswordStrength implements two validators: PasswordStrength::Base and PasswordStrength::Validators::Windows2008.
#
def validates_strength_of(*attr_names)
validates_with StrengthValidator, _merge_attributes(attr_names)
end
end
end
end