activemodel 4.2.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +49 -37
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -22
- data/lib/active_model/attribute/user_provided_default.rb +51 -0
- data/lib/active_model/attribute.rb +248 -0
- data/lib/active_model/attribute_assignment.rb +55 -0
- data/lib/active_model/attribute_methods.rb +150 -73
- data/lib/active_model/attribute_mutation_tracker.rb +181 -0
- data/lib/active_model/attribute_set/builder.rb +191 -0
- data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
- data/lib/active_model/attribute_set.rb +106 -0
- data/lib/active_model/attributes.rb +132 -0
- data/lib/active_model/callbacks.rb +31 -25
- data/lib/active_model/conversion.rb +20 -9
- data/lib/active_model/dirty.rb +142 -116
- data/lib/active_model/error.rb +207 -0
- data/lib/active_model/errors.rb +436 -202
- data/lib/active_model/forbidden_attributes_protection.rb +6 -3
- data/lib/active_model/gem_version.rb +5 -3
- data/lib/active_model/lint.rb +47 -42
- data/lib/active_model/locale/en.yml +2 -1
- data/lib/active_model/model.rb +7 -7
- data/lib/active_model/naming.rb +36 -18
- data/lib/active_model/nested_error.rb +22 -0
- data/lib/active_model/railtie.rb +8 -0
- data/lib/active_model/secure_password.rb +61 -67
- data/lib/active_model/serialization.rb +48 -17
- data/lib/active_model/serializers/json.rb +22 -13
- data/lib/active_model/translation.rb +5 -4
- data/lib/active_model/type/big_integer.rb +14 -0
- data/lib/active_model/type/binary.rb +52 -0
- data/lib/active_model/type/boolean.rb +46 -0
- data/lib/active_model/type/date.rb +52 -0
- data/lib/active_model/type/date_time.rb +46 -0
- data/lib/active_model/type/decimal.rb +69 -0
- data/lib/active_model/type/float.rb +35 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +49 -0
- data/lib/active_model/type/helpers/mutable.rb +20 -0
- data/lib/active_model/type/helpers/numeric.rb +48 -0
- data/lib/active_model/type/helpers/time_value.rb +90 -0
- data/lib/active_model/type/helpers/timezone.rb +19 -0
- data/lib/active_model/type/helpers.rb +7 -0
- data/lib/active_model/type/immutable_string.rb +35 -0
- data/lib/active_model/type/integer.rb +67 -0
- data/lib/active_model/type/registry.rb +70 -0
- data/lib/active_model/type/string.rb +35 -0
- data/lib/active_model/type/time.rb +46 -0
- data/lib/active_model/type/value.rb +133 -0
- data/lib/active_model/type.rb +53 -0
- data/lib/active_model/validations/absence.rb +6 -4
- data/lib/active_model/validations/acceptance.rb +72 -14
- data/lib/active_model/validations/callbacks.rb +23 -19
- data/lib/active_model/validations/clusivity.rb +18 -12
- data/lib/active_model/validations/confirmation.rb +27 -14
- data/lib/active_model/validations/exclusion.rb +7 -4
- data/lib/active_model/validations/format.rb +27 -27
- data/lib/active_model/validations/helper_methods.rb +15 -0
- data/lib/active_model/validations/inclusion.rb +8 -7
- data/lib/active_model/validations/length.rb +35 -32
- data/lib/active_model/validations/numericality.rb +72 -34
- data/lib/active_model/validations/presence.rb +3 -3
- data/lib/active_model/validations/validates.rb +17 -15
- data/lib/active_model/validations/with.rb +6 -12
- data/lib/active_model/validations.rb +58 -23
- data/lib/active_model/validator.rb +23 -17
- data/lib/active_model/version.rb +4 -2
- data/lib/active_model.rb +18 -11
- metadata +44 -25
- data/lib/active_model/serializers/xml.rb +0 -238
- data/lib/active_model/test_case.rb +0 -4
@@ -1,25 +1,82 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module ActiveModel
|
3
4
|
module Validations
|
4
5
|
class AcceptanceValidator < EachValidator # :nodoc:
|
5
6
|
def initialize(options)
|
6
|
-
super({ allow_nil: true, accept: "1" }.merge!(options))
|
7
|
+
super({ allow_nil: true, accept: ["1", true] }.merge!(options))
|
7
8
|
setup!(options[:class])
|
8
9
|
end
|
9
10
|
|
10
11
|
def validate_each(record, attribute, value)
|
11
|
-
unless value
|
12
|
-
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
|
12
|
+
unless acceptable_option?(value)
|
13
|
+
record.errors.add(attribute, :accepted, **options.except(:accept, :allow_nil))
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
def setup!(klass)
|
19
|
+
define_attributes = LazilyDefineAttributes.new(attributes)
|
20
|
+
klass.include(define_attributes) unless klass.included_modules.include?(define_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(attributes)
|
29
|
+
@attributes = attributes.map(&:to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def included(klass)
|
33
|
+
@lock = Mutex.new
|
34
|
+
mod = self
|
35
|
+
|
36
|
+
define_method(:respond_to_missing?) do |method_name, include_private = false|
|
37
|
+
mod.define_on(klass)
|
38
|
+
super(method_name, include_private) || mod.matches?(method_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
42
|
+
mod.define_on(klass)
|
43
|
+
if mod.matches?(method_name)
|
44
|
+
send(method_name, *args, &block)
|
45
|
+
else
|
46
|
+
super(method_name, *args, &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def matches?(method_name)
|
52
|
+
attr_name = method_name.to_s.chomp("=")
|
53
|
+
attributes.any? { |name| name == attr_name }
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_on(klass)
|
57
|
+
@lock&.synchronize do
|
58
|
+
return unless @lock
|
59
|
+
|
60
|
+
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
61
|
+
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
62
|
+
|
63
|
+
attr_reader(*attr_readers)
|
64
|
+
attr_writer(*attr_writers)
|
65
|
+
|
66
|
+
remove_method :respond_to_missing?
|
67
|
+
remove_method :method_missing
|
68
|
+
|
69
|
+
@lock = nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def ==(other)
|
74
|
+
self.class == other.class && attributes == other.attributes
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
attr_reader :attributes
|
79
|
+
end
|
23
80
|
end
|
24
81
|
|
25
82
|
module HelperMethods
|
@@ -38,15 +95,16 @@ module ActiveModel
|
|
38
95
|
# Configuration options:
|
39
96
|
# * <tt>:message</tt> - A custom error message (default is: "must be
|
40
97
|
# accepted").
|
41
|
-
# * <tt>:accept</tt> - Specifies value that is considered accepted.
|
42
|
-
#
|
43
|
-
# an
|
98
|
+
# * <tt>:accept</tt> - Specifies a value that is considered accepted.
|
99
|
+
# Also accepts an array of possible values. The default value is
|
100
|
+
# an array ["1", true], which makes it easy to relate to an HTML
|
101
|
+
# checkbox. This should be set to, or include, +true+ if you are validating
|
44
102
|
# a database column, since the attribute is typecast from "1" to +true+
|
45
103
|
# before validation.
|
46
104
|
#
|
47
105
|
# There is also a list of default options supported by every validator:
|
48
106
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
49
|
-
# See <tt>ActiveModel::
|
107
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information.
|
50
108
|
def validates_acceptance_of(*attr_names)
|
51
109
|
validates_with AcceptanceValidator, _merge_attributes(attr_names)
|
52
110
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveModel
|
2
4
|
module Validations
|
3
5
|
# == Active \Model \Validation \Callbacks
|
@@ -15,22 +17,20 @@ module ActiveModel
|
|
15
17
|
# after_validation :do_stuff_after_validation
|
16
18
|
# end
|
17
19
|
#
|
18
|
-
# Like other <tt>before_*</tt> callbacks if +before_validation+
|
19
|
-
# +
|
20
|
+
# Like other <tt>before_*</tt> callbacks if +before_validation+ throws
|
21
|
+
# +:abort+ then <tt>valid?</tt> will not be called.
|
20
22
|
module Callbacks
|
21
23
|
extend ActiveSupport::Concern
|
22
24
|
|
23
25
|
included do
|
24
26
|
include ActiveSupport::Callbacks
|
25
27
|
define_callbacks :validation,
|
26
|
-
terminator: ->(_,result) { result == false },
|
27
28
|
skip_after_callbacks_if_terminated: true,
|
28
29
|
scope: [:kind, :name]
|
29
30
|
end
|
30
31
|
|
31
32
|
module ClassMethods
|
32
|
-
# Defines a callback that will get called right before validation
|
33
|
-
# happens.
|
33
|
+
# Defines a callback that will get called right before validation.
|
34
34
|
#
|
35
35
|
# class Person
|
36
36
|
# include ActiveModel::Validations
|
@@ -54,19 +54,21 @@ module ActiveModel
|
|
54
54
|
# person.valid? # => true
|
55
55
|
# person.name # => "bob"
|
56
56
|
def before_validation(*args, &block)
|
57
|
-
options = args.
|
58
|
-
|
59
|
-
|
57
|
+
options = args.extract_options!
|
58
|
+
|
59
|
+
if options.key?(:on)
|
60
|
+
options = options.dup
|
60
61
|
options[:on] = Array(options[:on])
|
62
|
+
options[:if] = Array(options[:if])
|
61
63
|
options[:if].unshift ->(o) {
|
62
|
-
options[:on]
|
64
|
+
!(options[:on] & Array(o.validation_context)).empty?
|
63
65
|
}
|
64
66
|
end
|
65
|
-
|
67
|
+
|
68
|
+
set_callback(:validation, :before, *args, options, &block)
|
66
69
|
end
|
67
70
|
|
68
|
-
# Defines a callback that will get called right after validation
|
69
|
-
# happens.
|
71
|
+
# Defines a callback that will get called right after validation.
|
70
72
|
#
|
71
73
|
# class Person
|
72
74
|
# include ActiveModel::Validations
|
@@ -94,22 +96,24 @@ module ActiveModel
|
|
94
96
|
# person.status # => true
|
95
97
|
def after_validation(*args, &block)
|
96
98
|
options = args.extract_options!
|
99
|
+
options = options.dup
|
97
100
|
options[:prepend] = true
|
98
|
-
|
99
|
-
if options
|
101
|
+
|
102
|
+
if options.key?(:on)
|
100
103
|
options[:on] = Array(options[:on])
|
104
|
+
options[:if] = Array(options[:if])
|
101
105
|
options[:if].unshift ->(o) {
|
102
|
-
options[:on]
|
106
|
+
!(options[:on] & Array(o.validation_context)).empty?
|
103
107
|
}
|
104
108
|
end
|
105
|
-
|
109
|
+
|
110
|
+
set_callback(:validation, :after, *args, options, &block)
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
109
|
-
|
110
|
-
|
114
|
+
private
|
111
115
|
# Overwrite run validations to include callbacks.
|
112
|
-
def run_validations!
|
116
|
+
def run_validations!
|
113
117
|
_run_validation_callbacks { super }
|
114
118
|
end
|
115
119
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/range"
|
2
4
|
|
3
5
|
module ActiveModel
|
4
6
|
module Validations
|
@@ -13,31 +15,35 @@ module ActiveModel
|
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
16
|
-
|
17
18
|
def include?(record, value)
|
18
19
|
members = if delimiter.respond_to?(:call)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
delimiter.call(record)
|
21
|
+
elsif delimiter.respond_to?(:to_sym)
|
22
|
+
record.send(delimiter)
|
23
|
+
else
|
24
|
+
delimiter
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
+
if value.is_a?(Array)
|
28
|
+
value.all? { |v| members.public_send(inclusion_method(members), v) }
|
29
|
+
else
|
30
|
+
members.public_send(inclusion_method(members), value)
|
31
|
+
end
|
27
32
|
end
|
28
33
|
|
29
34
|
def delimiter
|
30
35
|
@delimiter ||= options[:in] || options[:within]
|
31
36
|
end
|
32
37
|
|
33
|
-
#
|
38
|
+
# After Ruby 2.2, <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
|
34
39
|
# possible values in the range for equality, which is slower but more accurate.
|
35
40
|
# <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
|
36
|
-
# endpoints, which is fast but is only accurate on Numeric, Time,
|
41
|
+
# endpoints, which is fast but is only accurate on Numeric, Time, Date,
|
42
|
+
# or DateTime ranges.
|
37
43
|
def inclusion_method(enumerable)
|
38
44
|
if enumerable.is_a? Range
|
39
45
|
case enumerable.first
|
40
|
-
when Numeric, Time, DateTime
|
46
|
+
when Numeric, Time, DateTime, Date
|
41
47
|
:cover?
|
42
48
|
else
|
43
49
|
:include?
|
@@ -1,29 +1,40 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module ActiveModel
|
3
4
|
module Validations
|
4
5
|
class ConfirmationValidator < EachValidator # :nodoc:
|
5
6
|
def initialize(options)
|
6
|
-
super
|
7
|
+
super({ case_sensitive: true }.merge!(options))
|
7
8
|
setup!(options[:class])
|
8
9
|
end
|
9
10
|
|
10
11
|
def validate_each(record, attribute, value)
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
unless (confirmed = record.public_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
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
17
20
|
private
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def setup!(klass)
|
22
|
+
klass.attr_reader(*attributes.map do |attribute|
|
23
|
+
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
|
24
|
+
end.compact)
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
klass.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
|
27
38
|
end
|
28
39
|
|
29
40
|
module HelperMethods
|
@@ -55,10 +66,12 @@ module ActiveModel
|
|
55
66
|
# Configuration options:
|
56
67
|
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
|
57
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).
|
58
71
|
#
|
59
72
|
# There is also a list of default options supported by every validator:
|
60
73
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
61
|
-
# See <tt>ActiveModel::
|
74
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
62
75
|
def validates_confirmation_of(*attr_names)
|
63
76
|
validates_with ConfirmationValidator, _merge_attributes(attr_names)
|
64
77
|
end
|
@@ -1,14 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_model/validations/clusivity"
|
2
4
|
|
3
5
|
module ActiveModel
|
4
|
-
|
5
6
|
module Validations
|
6
7
|
class ExclusionValidator < EachValidator # :nodoc:
|
7
8
|
include Clusivity
|
8
9
|
|
9
10
|
def validate_each(record, attribute, value)
|
10
11
|
if include?(record, value)
|
11
|
-
record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(value: value))
|
12
|
+
record.errors.add(attribute, :exclusion, **options.except(:in, :within).merge!(value: value))
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
@@ -29,7 +30,9 @@ module ActiveModel
|
|
29
30
|
# Configuration options:
|
30
31
|
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
|
31
32
|
# be part of. This can be supplied as a proc, lambda or symbol which returns an
|
32
|
-
# enumerable. If the enumerable is a range the test
|
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.
|
33
36
|
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
|
34
37
|
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
|
35
38
|
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
|
@@ -37,7 +40,7 @@ module ActiveModel
|
|
37
40
|
#
|
38
41
|
# There is also a list of default options supported by every validator:
|
39
42
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
40
|
-
# See <tt>ActiveModel::
|
43
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
41
44
|
def validates_exclusion_of(*attr_names)
|
42
45
|
validates_with ExclusionValidator, _merge_attributes(attr_names)
|
43
46
|
end
|
@@ -1,14 +1,15 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module ActiveModel
|
3
4
|
module Validations
|
4
5
|
class FormatValidator < EachValidator # :nodoc:
|
5
6
|
def validate_each(record, attribute, value)
|
6
7
|
if options[:with]
|
7
8
|
regexp = option_call(record, :with)
|
8
|
-
record_error(record, attribute, :with, value)
|
9
|
+
record_error(record, attribute, :with, value) unless regexp.match?(value.to_s)
|
9
10
|
elsif options[:without]
|
10
11
|
regexp = option_call(record, :without)
|
11
|
-
record_error(record, attribute, :without, value) if value.to_s
|
12
|
+
record_error(record, attribute, :without, value) if regexp.match?(value.to_s)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
@@ -22,34 +23,33 @@ module ActiveModel
|
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
26
|
+
def option_call(record, name)
|
27
|
+
option = options[name]
|
28
|
+
option.respond_to?(:call) ? option.call(record) : option
|
29
|
+
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
def record_error(record, attribute, name, value)
|
32
|
-
record.errors.add(attribute, :invalid, options.except(name).merge!(value: value))
|
33
|
-
end
|
31
|
+
def record_error(record, attribute, name, value)
|
32
|
+
record.errors.add(attribute, :invalid, **options.except(name).merge!(value: value))
|
33
|
+
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
def check_options_validity(name)
|
36
|
+
if option = options[name]
|
37
|
+
if option.is_a?(Regexp)
|
38
|
+
if options[:multiline] != true && regexp_using_multiline_anchors?(option)
|
39
|
+
raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
|
40
|
+
"which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
|
41
|
+
":multiline => true option?"
|
42
|
+
end
|
43
|
+
elsif !option.respond_to?(:call)
|
44
|
+
raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
|
42
45
|
end
|
43
|
-
elsif !option.respond_to?(:call)
|
44
|
-
raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
|
45
46
|
end
|
46
47
|
end
|
47
|
-
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
def regexp_using_multiline_anchors?(regexp)
|
50
|
+
source = regexp.source
|
51
|
+
source.start_with?("^") || (source.end_with?("$") && !source.end_with?("\\$"))
|
52
|
+
end
|
53
53
|
end
|
54
54
|
|
55
55
|
module HelperMethods
|
@@ -77,7 +77,7 @@ module ActiveModel
|
|
77
77
|
# with: ->(person) { person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
|
78
78
|
# end
|
79
79
|
#
|
80
|
-
# Note: use <tt>\A</tt> and <tt>\
|
80
|
+
# Note: use <tt>\A</tt> and <tt>\z</tt> to match the start and end of the
|
81
81
|
# string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
|
82
82
|
#
|
83
83
|
# Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass
|
@@ -104,7 +104,7 @@ module ActiveModel
|
|
104
104
|
#
|
105
105
|
# There is also a list of default options supported by every validator:
|
106
106
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
107
|
-
# See <tt>ActiveModel::
|
107
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
108
108
|
def validates_format_of(*attr_names)
|
109
109
|
validates_with FormatValidator, _merge_attributes(attr_names)
|
110
110
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
module HelperMethods # :nodoc:
|
6
|
+
private
|
7
|
+
def _merge_attributes(attr_names)
|
8
|
+
options = attr_names.extract_options!.symbolize_keys
|
9
|
+
attr_names.flatten!
|
10
|
+
options[:attributes] = attr_names
|
11
|
+
options
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,14 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_model/validations/clusivity"
|
2
4
|
|
3
5
|
module ActiveModel
|
4
|
-
|
5
6
|
module Validations
|
6
7
|
class InclusionValidator < EachValidator # :nodoc:
|
7
8
|
include Clusivity
|
8
9
|
|
9
10
|
def validate_each(record, attribute, value)
|
10
11
|
unless include?(record, value)
|
11
|
-
record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(value: value))
|
12
|
+
record.errors.add(attribute, :inclusion, **options.except(:in, :within).merge!(value: value))
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
@@ -18,7 +19,7 @@ module ActiveModel
|
|
18
19
|
# particular enumerable object.
|
19
20
|
#
|
20
21
|
# class Person < ActiveRecord::Base
|
21
|
-
# validates_inclusion_of :
|
22
|
+
# validates_inclusion_of :role, in: %w( admin contributor )
|
22
23
|
# validates_inclusion_of :age, in: 0..99
|
23
24
|
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
|
24
25
|
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
|
@@ -28,16 +29,16 @@ module ActiveModel
|
|
28
29
|
# Configuration options:
|
29
30
|
# * <tt>:in</tt> - An enumerable object of available items. This can be
|
30
31
|
# supplied as a proc, lambda or symbol which returns an enumerable. If the
|
31
|
-
# enumerable is a numerical range the test is performed
|
32
|
-
# otherwise with <tt>include?</tt>. When using
|
33
|
-
# under validation is passed as an argument.
|
32
|
+
# enumerable is a numerical, time or datetime range the test is performed
|
33
|
+
# with <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>. When using
|
34
|
+
# a proc or lambda the instance under validation is passed as an argument.
|
34
35
|
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
|
35
36
|
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
|
36
37
|
# not included in the list").
|
37
38
|
#
|
38
39
|
# There is also a list of default options supported by every validator:
|
39
40
|
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
|
40
|
-
# See <tt>ActiveModel::
|
41
|
+
# See <tt>ActiveModel::Validations#validates</tt> for more information
|
41
42
|
def validates_inclusion_of(*attr_names)
|
42
43
|
validates_with InclusionValidator, _merge_attributes(attr_names)
|
43
44
|
end
|