activemodel 4.2.0 → 6.1.0
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 +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
|