activemodel 5.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +114 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +264 -0
- data/lib/active_model.rb +77 -0
- data/lib/active_model/attribute.rb +248 -0
- data/lib/active_model/attribute/user_provided_default.rb +52 -0
- data/lib/active_model/attribute_assignment.rb +57 -0
- data/lib/active_model/attribute_methods.rb +478 -0
- data/lib/active_model/attribute_mutation_tracker.rb +124 -0
- data/lib/active_model/attribute_set.rb +114 -0
- data/lib/active_model/attribute_set/builder.rb +126 -0
- data/lib/active_model/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_model/attributes.rb +111 -0
- data/lib/active_model/callbacks.rb +153 -0
- data/lib/active_model/conversion.rb +111 -0
- data/lib/active_model/dirty.rb +343 -0
- data/lib/active_model/errors.rb +517 -0
- data/lib/active_model/forbidden_attributes_protection.rb +31 -0
- data/lib/active_model/gem_version.rb +17 -0
- data/lib/active_model/lint.rb +118 -0
- data/lib/active_model/locale/en.yml +36 -0
- data/lib/active_model/model.rb +99 -0
- data/lib/active_model/naming.rb +318 -0
- data/lib/active_model/railtie.rb +14 -0
- data/lib/active_model/secure_password.rb +129 -0
- data/lib/active_model/serialization.rb +192 -0
- data/lib/active_model/serializers/json.rb +146 -0
- data/lib/active_model/translation.rb +70 -0
- data/lib/active_model/type.rb +53 -0
- data/lib/active_model/type/big_integer.rb +15 -0
- data/lib/active_model/type/binary.rb +52 -0
- data/lib/active_model/type/boolean.rb +38 -0
- data/lib/active_model/type/date.rb +57 -0
- data/lib/active_model/type/date_time.rb +51 -0
- data/lib/active_model/type/decimal.rb +70 -0
- data/lib/active_model/type/float.rb +36 -0
- data/lib/active_model/type/helpers.rb +7 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +41 -0
- data/lib/active_model/type/helpers/mutable.rb +20 -0
- data/lib/active_model/type/helpers/numeric.rb +37 -0
- data/lib/active_model/type/helpers/time_value.rb +68 -0
- data/lib/active_model/type/helpers/timezone.rb +19 -0
- data/lib/active_model/type/immutable_string.rb +32 -0
- data/lib/active_model/type/integer.rb +70 -0
- data/lib/active_model/type/registry.rb +70 -0
- data/lib/active_model/type/string.rb +26 -0
- data/lib/active_model/type/time.rb +51 -0
- data/lib/active_model/type/value.rb +126 -0
- data/lib/active_model/validations.rb +439 -0
- data/lib/active_model/validations/absence.rb +33 -0
- data/lib/active_model/validations/acceptance.rb +106 -0
- data/lib/active_model/validations/callbacks.rb +122 -0
- data/lib/active_model/validations/clusivity.rb +54 -0
- data/lib/active_model/validations/confirmation.rb +80 -0
- data/lib/active_model/validations/exclusion.rb +49 -0
- data/lib/active_model/validations/format.rb +114 -0
- data/lib/active_model/validations/helper_methods.rb +15 -0
- data/lib/active_model/validations/inclusion.rb +47 -0
- data/lib/active_model/validations/length.rb +129 -0
- data/lib/active_model/validations/numericality.rb +189 -0
- data/lib/active_model/validations/presence.rb +39 -0
- data/lib/active_model/validations/validates.rb +174 -0
- data/lib/active_model/validations/with.rb +147 -0
- data/lib/active_model/validator.rb +183 -0
- data/lib/active_model/version.rb +10 -0
- metadata +125 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
# == \Active \Model Absence Validator
|
6
|
+
class AbsenceValidator < EachValidator #:nodoc:
|
7
|
+
def validate_each(record, attr_name, value)
|
8
|
+
record.errors.add(attr_name, :present, options) if value.present?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module HelperMethods
|
13
|
+
# Validates that the specified attributes are blank (as defined by
|
14
|
+
# Object#blank?). Happens by default on save.
|
15
|
+
#
|
16
|
+
# class Person < ActiveRecord::Base
|
17
|
+
# validates_absence_of :first_name
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# The first_name attribute must be in the object and it must be blank.
|
21
|
+
#
|
22
|
+
# Configuration options:
|
23
|
+
# * <tt>:message</tt> - A custom error message (default is: "must be blank").
|
24
|
+
#
|
25
|
+
# There is also a list of default options supported by every validator:
|
26
|
+
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
27
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
28
|
+
def validates_absence_of(*attr_names)
|
29
|
+
validates_with AbsenceValidator, _merge_attributes(attr_names)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class AcceptanceValidator < EachValidator # :nodoc:
|
6
|
+
def initialize(options)
|
7
|
+
super({ allow_nil: true, accept: ["1", true] }.merge!(options))
|
8
|
+
setup!(options[:class])
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_each(record, attribute, value)
|
12
|
+
unless acceptable_option?(value)
|
13
|
+
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def setup!(klass)
|
20
|
+
klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
|
21
|
+
end
|
22
|
+
|
23
|
+
def acceptable_option?(value)
|
24
|
+
Array(options[:accept]).include?(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
class LazilyDefineAttributes < Module
|
28
|
+
def initialize(attribute_definition)
|
29
|
+
define_method(:respond_to_missing?) do |method_name, include_private = false|
|
30
|
+
super(method_name, include_private) || attribute_definition.matches?(method_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
34
|
+
if attribute_definition.matches?(method_name)
|
35
|
+
attribute_definition.define_on(self.class)
|
36
|
+
send(method_name, *args, &block)
|
37
|
+
else
|
38
|
+
super(method_name, *args, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class AttributeDefinition
|
45
|
+
def initialize(attributes)
|
46
|
+
@attributes = attributes.map(&:to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
def matches?(method_name)
|
50
|
+
attr_name = convert_to_reader_name(method_name)
|
51
|
+
attributes.include?(attr_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def define_on(klass)
|
55
|
+
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
56
|
+
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
57
|
+
klass.send(:attr_reader, *attr_readers)
|
58
|
+
klass.send(:attr_writer, *attr_writers)
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
62
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
63
|
+
protected
|
64
|
+
|
65
|
+
attr_reader :attributes
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def convert_to_reader_name(method_name)
|
70
|
+
method_name.to_s.chomp("=")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module HelperMethods
|
76
|
+
# Encapsulates the pattern of wanting to validate the acceptance of a
|
77
|
+
# terms of service check box (or similar agreement).
|
78
|
+
#
|
79
|
+
# class Person < ActiveRecord::Base
|
80
|
+
# validates_acceptance_of :terms_of_service
|
81
|
+
# validates_acceptance_of :eula, message: 'must be abided'
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# If the database column does not exist, the +terms_of_service+ attribute
|
85
|
+
# is entirely virtual. This check is performed only if +terms_of_service+
|
86
|
+
# is not +nil+ and by default on save.
|
87
|
+
#
|
88
|
+
# Configuration options:
|
89
|
+
# * <tt>:message</tt> - A custom error message (default is: "must be
|
90
|
+
# accepted").
|
91
|
+
# * <tt>:accept</tt> - Specifies a value that is considered accepted.
|
92
|
+
# Also accepts an array of possible values. The default value is
|
93
|
+
# an array ["1", true], which makes it easy to relate to an HTML
|
94
|
+
# checkbox. This should be set to, or include, +true+ if you are validating
|
95
|
+
# a database column, since the attribute is typecast from "1" to +true+
|
96
|
+
# before validation.
|
97
|
+
#
|
98
|
+
# There is also a list of default options supported by every validator:
|
99
|
+
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
100
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information.
|
101
|
+
def validates_acceptance_of(*attr_names)
|
102
|
+
validates_with AcceptanceValidator, _merge_attributes(attr_names)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
# == Active \Model \Validation \Callbacks
|
6
|
+
#
|
7
|
+
# Provides an interface for any class to have +before_validation+ and
|
8
|
+
# +after_validation+ callbacks.
|
9
|
+
#
|
10
|
+
# First, include ActiveModel::Validations::Callbacks from the class you are
|
11
|
+
# creating:
|
12
|
+
#
|
13
|
+
# class MyModel
|
14
|
+
# include ActiveModel::Validations::Callbacks
|
15
|
+
#
|
16
|
+
# before_validation :do_stuff_before_validation
|
17
|
+
# after_validation :do_stuff_after_validation
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Like other <tt>before_*</tt> callbacks if +before_validation+ throws
|
21
|
+
# +:abort+ then <tt>valid?</tt> will not be called.
|
22
|
+
module Callbacks
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
included do
|
26
|
+
include ActiveSupport::Callbacks
|
27
|
+
define_callbacks :validation,
|
28
|
+
skip_after_callbacks_if_terminated: true,
|
29
|
+
scope: [:kind, :name]
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
# Defines a callback that will get called right before validation.
|
34
|
+
#
|
35
|
+
# class Person
|
36
|
+
# include ActiveModel::Validations
|
37
|
+
# include ActiveModel::Validations::Callbacks
|
38
|
+
#
|
39
|
+
# attr_accessor :name
|
40
|
+
#
|
41
|
+
# validates_length_of :name, maximum: 6
|
42
|
+
#
|
43
|
+
# before_validation :remove_whitespaces
|
44
|
+
#
|
45
|
+
# private
|
46
|
+
#
|
47
|
+
# def remove_whitespaces
|
48
|
+
# name.strip!
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# person = Person.new
|
53
|
+
# person.name = ' bob '
|
54
|
+
# person.valid? # => true
|
55
|
+
# person.name # => "bob"
|
56
|
+
def before_validation(*args, &block)
|
57
|
+
options = args.extract_options!
|
58
|
+
|
59
|
+
if options.key?(:on)
|
60
|
+
options = options.dup
|
61
|
+
options[:on] = Array(options[:on])
|
62
|
+
options[:if] = Array(options[:if])
|
63
|
+
options[:if].unshift ->(o) {
|
64
|
+
!(options[:on] & Array(o.validation_context)).empty?
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
set_callback(:validation, :before, *args, options, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Defines a callback that will get called right after validation.
|
72
|
+
#
|
73
|
+
# class Person
|
74
|
+
# include ActiveModel::Validations
|
75
|
+
# include ActiveModel::Validations::Callbacks
|
76
|
+
#
|
77
|
+
# attr_accessor :name, :status
|
78
|
+
#
|
79
|
+
# validates_presence_of :name
|
80
|
+
#
|
81
|
+
# after_validation :set_status
|
82
|
+
#
|
83
|
+
# private
|
84
|
+
#
|
85
|
+
# def set_status
|
86
|
+
# self.status = errors.empty?
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# person = Person.new
|
91
|
+
# person.name = ''
|
92
|
+
# person.valid? # => false
|
93
|
+
# person.status # => false
|
94
|
+
# person.name = 'bob'
|
95
|
+
# person.valid? # => true
|
96
|
+
# person.status # => true
|
97
|
+
def after_validation(*args, &block)
|
98
|
+
options = args.extract_options!
|
99
|
+
options = options.dup
|
100
|
+
options[:prepend] = true
|
101
|
+
|
102
|
+
if options.key?(:on)
|
103
|
+
options[:on] = Array(options[:on])
|
104
|
+
options[:if] = Array(options[:if])
|
105
|
+
options[:if].unshift ->(o) {
|
106
|
+
!(options[:on] & Array(o.validation_context)).empty?
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
set_callback(:validation, :after, *args, options, &block)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
# Overwrite run validations to include callbacks.
|
117
|
+
def run_validations!
|
118
|
+
_run_validation_callbacks { super }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/range"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module Validations
|
7
|
+
module Clusivity #:nodoc:
|
8
|
+
ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
|
9
|
+
"and must be supplied as the :in (or :within) option of the configuration hash"
|
10
|
+
|
11
|
+
def check_validity!
|
12
|
+
unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
|
13
|
+
raise ArgumentError, ERROR_MESSAGE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def include?(record, value)
|
20
|
+
members = if delimiter.respond_to?(:call)
|
21
|
+
delimiter.call(record)
|
22
|
+
elsif delimiter.respond_to?(:to_sym)
|
23
|
+
record.send(delimiter)
|
24
|
+
else
|
25
|
+
delimiter
|
26
|
+
end
|
27
|
+
|
28
|
+
members.send(inclusion_method(members), value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delimiter
|
32
|
+
@delimiter ||= options[:in] || options[:within]
|
33
|
+
end
|
34
|
+
|
35
|
+
# In Ruby 2.2 <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
|
36
|
+
# possible values in the range for equality, which is slower but more accurate.
|
37
|
+
# <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
|
38
|
+
# endpoints, which is fast but is only accurate on Numeric, Time, Date,
|
39
|
+
# or DateTime ranges.
|
40
|
+
def inclusion_method(enumerable)
|
41
|
+
if enumerable.is_a? Range
|
42
|
+
case enumerable.first
|
43
|
+
when Numeric, Time, DateTime, Date
|
44
|
+
:cover?
|
45
|
+
else
|
46
|
+
:include?
|
47
|
+
end
|
48
|
+
else
|
49
|
+
:include?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class ConfirmationValidator < EachValidator # :nodoc:
|
6
|
+
def initialize(options)
|
7
|
+
super({ case_sensitive: true }.merge!(options))
|
8
|
+
setup!(options[:class])
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_each(record, attribute, value)
|
12
|
+
unless (confirmed = record.send("#{attribute}_confirmation")).nil?
|
13
|
+
unless confirmation_value_equal?(record, attribute, value, confirmed)
|
14
|
+
human_attribute_name = record.class.human_attribute_name(attribute)
|
15
|
+
record.errors.add(:"#{attribute}_confirmation", :confirmation, options.except(:case_sensitive).merge!(attribute: human_attribute_name))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def setup!(klass)
|
22
|
+
klass.send(:attr_reader, *attributes.map do |attribute|
|
23
|
+
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
|
24
|
+
end.compact)
|
25
|
+
|
26
|
+
klass.send(:attr_writer, *attributes.map do |attribute|
|
27
|
+
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
|
28
|
+
end.compact)
|
29
|
+
end
|
30
|
+
|
31
|
+
def confirmation_value_equal?(record, attribute, value, confirmed)
|
32
|
+
if !options[:case_sensitive] && value.is_a?(String)
|
33
|
+
value.casecmp(confirmed) == 0
|
34
|
+
else
|
35
|
+
value == confirmed
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module HelperMethods
|
41
|
+
# Encapsulates the pattern of wanting to validate a password or email
|
42
|
+
# address field with a confirmation.
|
43
|
+
#
|
44
|
+
# Model:
|
45
|
+
# class Person < ActiveRecord::Base
|
46
|
+
# validates_confirmation_of :user_name, :password
|
47
|
+
# validates_confirmation_of :email_address,
|
48
|
+
# message: 'should match confirmation'
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# View:
|
52
|
+
# <%= password_field "person", "password" %>
|
53
|
+
# <%= password_field "person", "password_confirmation" %>
|
54
|
+
#
|
55
|
+
# The added +password_confirmation+ attribute is virtual; it exists only
|
56
|
+
# as an in-memory attribute for validating the password. To achieve this,
|
57
|
+
# the validation adds accessors to the model for the confirmation
|
58
|
+
# attribute.
|
59
|
+
#
|
60
|
+
# NOTE: This check is performed only if +password_confirmation+ is not
|
61
|
+
# +nil+. To require confirmation, make sure to add a presence check for
|
62
|
+
# the confirmation attribute:
|
63
|
+
#
|
64
|
+
# validates_presence_of :password_confirmation, if: :password_changed?
|
65
|
+
#
|
66
|
+
# Configuration options:
|
67
|
+
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
|
68
|
+
# <tt>%{translated_attribute_name}</tt>").
|
69
|
+
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
|
70
|
+
# non-text columns (+true+ by default).
|
71
|
+
#
|
72
|
+
# There is also a list of default options supported by every validator:
|
73
|
+
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
74
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
75
|
+
def validates_confirmation_of(*attr_names)
|
76
|
+
validates_with ConfirmationValidator, _merge_attributes(attr_names)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model/validations/clusivity"
|
4
|
+
|
5
|
+
module ActiveModel
|
6
|
+
module Validations
|
7
|
+
class ExclusionValidator < EachValidator # :nodoc:
|
8
|
+
include Clusivity
|
9
|
+
|
10
|
+
def validate_each(record, attribute, value)
|
11
|
+
if include?(record, value)
|
12
|
+
record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(value: value))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module HelperMethods
|
18
|
+
# Validates that the value of the specified attribute is not in a
|
19
|
+
# particular enumerable object.
|
20
|
+
#
|
21
|
+
# class Person < ActiveRecord::Base
|
22
|
+
# validates_exclusion_of :username, in: %w( admin superuser ), message: "You don't belong here"
|
23
|
+
# validates_exclusion_of :age, in: 30..60, message: 'This site is only for under 30 and over 60'
|
24
|
+
# validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
|
25
|
+
# validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
|
26
|
+
# message: 'should not be the same as your username or first name'
|
27
|
+
# validates_exclusion_of :karma, in: :reserved_karmas
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Configuration options:
|
31
|
+
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
|
32
|
+
# be part of. This can be supplied as a proc, lambda or symbol which returns an
|
33
|
+
# enumerable. If the enumerable is a numerical, time or datetime range the test
|
34
|
+
# is performed with <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>. When
|
35
|
+
# using a proc or lambda the instance under validation is passed as an argument.
|
36
|
+
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
|
37
|
+
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
|
38
|
+
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
|
39
|
+
# reserved").
|
40
|
+
#
|
41
|
+
# There is also a list of default options supported by every validator:
|
42
|
+
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
43
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
44
|
+
def validates_exclusion_of(*attr_names)
|
45
|
+
validates_with ExclusionValidator, _merge_attributes(attr_names)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|