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.
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