activemodel 4.2.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +49 -37
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -22
  5. data/lib/active_model/attribute/user_provided_default.rb +51 -0
  6. data/lib/active_model/attribute.rb +248 -0
  7. data/lib/active_model/attribute_assignment.rb +55 -0
  8. data/lib/active_model/attribute_methods.rb +150 -73
  9. data/lib/active_model/attribute_mutation_tracker.rb +181 -0
  10. data/lib/active_model/attribute_set/builder.rb +191 -0
  11. data/lib/active_model/attribute_set/yaml_encoder.rb +40 -0
  12. data/lib/active_model/attribute_set.rb +106 -0
  13. data/lib/active_model/attributes.rb +132 -0
  14. data/lib/active_model/callbacks.rb +31 -25
  15. data/lib/active_model/conversion.rb +20 -9
  16. data/lib/active_model/dirty.rb +142 -116
  17. data/lib/active_model/error.rb +207 -0
  18. data/lib/active_model/errors.rb +436 -202
  19. data/lib/active_model/forbidden_attributes_protection.rb +6 -3
  20. data/lib/active_model/gem_version.rb +5 -3
  21. data/lib/active_model/lint.rb +47 -42
  22. data/lib/active_model/locale/en.yml +2 -1
  23. data/lib/active_model/model.rb +7 -7
  24. data/lib/active_model/naming.rb +36 -18
  25. data/lib/active_model/nested_error.rb +22 -0
  26. data/lib/active_model/railtie.rb +8 -0
  27. data/lib/active_model/secure_password.rb +61 -67
  28. data/lib/active_model/serialization.rb +48 -17
  29. data/lib/active_model/serializers/json.rb +22 -13
  30. data/lib/active_model/translation.rb +5 -4
  31. data/lib/active_model/type/big_integer.rb +14 -0
  32. data/lib/active_model/type/binary.rb +52 -0
  33. data/lib/active_model/type/boolean.rb +46 -0
  34. data/lib/active_model/type/date.rb +52 -0
  35. data/lib/active_model/type/date_time.rb +46 -0
  36. data/lib/active_model/type/decimal.rb +69 -0
  37. data/lib/active_model/type/float.rb +35 -0
  38. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +49 -0
  39. data/lib/active_model/type/helpers/mutable.rb +20 -0
  40. data/lib/active_model/type/helpers/numeric.rb +48 -0
  41. data/lib/active_model/type/helpers/time_value.rb +90 -0
  42. data/lib/active_model/type/helpers/timezone.rb +19 -0
  43. data/lib/active_model/type/helpers.rb +7 -0
  44. data/lib/active_model/type/immutable_string.rb +35 -0
  45. data/lib/active_model/type/integer.rb +67 -0
  46. data/lib/active_model/type/registry.rb +70 -0
  47. data/lib/active_model/type/string.rb +35 -0
  48. data/lib/active_model/type/time.rb +46 -0
  49. data/lib/active_model/type/value.rb +133 -0
  50. data/lib/active_model/type.rb +53 -0
  51. data/lib/active_model/validations/absence.rb +6 -4
  52. data/lib/active_model/validations/acceptance.rb +72 -14
  53. data/lib/active_model/validations/callbacks.rb +23 -19
  54. data/lib/active_model/validations/clusivity.rb +18 -12
  55. data/lib/active_model/validations/confirmation.rb +27 -14
  56. data/lib/active_model/validations/exclusion.rb +7 -4
  57. data/lib/active_model/validations/format.rb +27 -27
  58. data/lib/active_model/validations/helper_methods.rb +15 -0
  59. data/lib/active_model/validations/inclusion.rb +8 -7
  60. data/lib/active_model/validations/length.rb +35 -32
  61. data/lib/active_model/validations/numericality.rb +72 -34
  62. data/lib/active_model/validations/presence.rb +3 -3
  63. data/lib/active_model/validations/validates.rb +17 -15
  64. data/lib/active_model/validations/with.rb +6 -12
  65. data/lib/active_model/validations.rb +58 -23
  66. data/lib/active_model/validator.rb +23 -17
  67. data/lib/active_model/version.rb +4 -2
  68. data/lib/active_model.rb +18 -11
  69. metadata +44 -25
  70. data/lib/active_model/serializers/xml.rb +0 -238
  71. data/lib/active_model/test_case.rb +0 -4
@@ -1,25 +1,82 @@
1
- module ActiveModel
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 == options[:accept]
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
- def setup!(klass)
18
- attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
19
- attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
20
- klass.send(:attr_reader, *attr_readers)
21
- klass.send(:attr_writer, *attr_writers)
22
- end
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
- # The default value is a string "1", which makes it easy to relate to
43
- # an HTML checkbox. This should be set to +true+ if you are validating
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::Validation#validates</tt> for more information.
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+ returns
19
- # +false+ then <tt>valid?</tt> will not be called.
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.last
58
- if options.is_a?(Hash) && options[:on]
59
- options[:if] = Array(options[:if])
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].include? o.validation_context
64
+ !(options[:on] & Array(o.validation_context)).empty?
63
65
  }
64
66
  end
65
- set_callback(:validation, :before, *args, &block)
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
- options[:if] = Array(options[:if])
99
- if options[:on]
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].include? o.validation_context
106
+ !(options[:on] & Array(o.validation_context)).empty?
103
107
  }
104
108
  end
105
- set_callback(:validation, :after, *(args << options), &block)
109
+
110
+ set_callback(:validation, :after, *args, options, &block)
106
111
  end
107
112
  end
108
113
 
109
- protected
110
-
114
+ private
111
115
  # Overwrite run validations to include callbacks.
112
- def run_validations! #:nodoc:
116
+ def run_validations!
113
117
  _run_validation_callbacks { super }
114
118
  end
115
119
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/range'
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
- delimiter.call(record)
20
- elsif delimiter.respond_to?(:to_sym)
21
- record.send(delimiter)
22
- else
23
- delimiter
24
- end
20
+ delimiter.call(record)
21
+ elsif delimiter.respond_to?(:to_sym)
22
+ record.send(delimiter)
23
+ else
24
+ delimiter
25
+ end
25
26
 
26
- members.send(inclusion_method(members), value)
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
- # In Ruby 1.9 <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
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, or DateTime ranges.
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
- module ActiveModel
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
- if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
12
- human_attribute_name = record.class.human_attribute_name(attribute)
13
- record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(attribute: human_attribute_name))
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
- def setup!(klass)
19
- klass.send(:attr_reader, *attributes.map do |attribute|
20
- :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
21
- end.compact)
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
- klass.send(:attr_writer, *attributes.map do |attribute|
24
- :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
25
- end.compact)
26
- end
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::Validation#validates</tt> for more information
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 is performed with
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::Validation#validates</tt> for more information
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
- module ActiveModel
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) if value.to_s !~ regexp
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 =~ regexp
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
- def option_call(record, name)
27
- option = options[name]
28
- option.respond_to?(:call) ? option.call(record) : option
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
- 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?"
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
- def regexp_using_multiline_anchors?(regexp)
50
- source = regexp.source
51
- source.start_with?("^") || (source.end_with?("$") && !source.end_with?("\\$"))
52
- end
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>\Z</tt> to match the start and end of the
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::Validation#validates</tt> for more information
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 :gender, in: %w( m f )
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 with <tt>Range#cover?</tt>,
32
- # otherwise with <tt>include?</tt>. When using a proc or lambda the instance
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::Validation#validates</tt> for more information
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