activemodel 5.2.8.1 → 6.1.6.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +91 -97
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +6 -4
  5. data/lib/active_model/attribute/user_provided_default.rb +1 -2
  6. data/lib/active_model/attribute.rb +21 -21
  7. data/lib/active_model/attribute_assignment.rb +4 -6
  8. data/lib/active_model/attribute_methods.rb +117 -40
  9. data/lib/active_model/attribute_mutation_tracker.rb +90 -33
  10. data/lib/active_model/attribute_set/builder.rb +81 -16
  11. data/lib/active_model/attribute_set/yaml_encoder.rb +1 -2
  12. data/lib/active_model/attribute_set.rb +20 -28
  13. data/lib/active_model/attributes.rb +65 -44
  14. data/lib/active_model/callbacks.rb +11 -9
  15. data/lib/active_model/conversion.rb +1 -1
  16. data/lib/active_model/dirty.rb +51 -101
  17. data/lib/active_model/error.rb +207 -0
  18. data/lib/active_model/errors.rb +347 -155
  19. data/lib/active_model/gem_version.rb +3 -3
  20. data/lib/active_model/lint.rb +1 -1
  21. data/lib/active_model/naming.rb +22 -7
  22. data/lib/active_model/nested_error.rb +22 -0
  23. data/lib/active_model/railtie.rb +6 -0
  24. data/lib/active_model/secure_password.rb +55 -55
  25. data/lib/active_model/serialization.rb +9 -7
  26. data/lib/active_model/serializers/json.rb +17 -9
  27. data/lib/active_model/translation.rb +1 -1
  28. data/lib/active_model/type/big_integer.rb +0 -1
  29. data/lib/active_model/type/binary.rb +1 -1
  30. data/lib/active_model/type/boolean.rb +0 -1
  31. data/lib/active_model/type/date.rb +0 -5
  32. data/lib/active_model/type/date_time.rb +3 -8
  33. data/lib/active_model/type/decimal.rb +0 -1
  34. data/lib/active_model/type/float.rb +2 -3
  35. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +14 -6
  36. data/lib/active_model/type/helpers/numeric.rb +17 -6
  37. data/lib/active_model/type/helpers/time_value.rb +37 -15
  38. data/lib/active_model/type/helpers/timezone.rb +1 -1
  39. data/lib/active_model/type/immutable_string.rb +14 -11
  40. data/lib/active_model/type/integer.rb +15 -18
  41. data/lib/active_model/type/registry.rb +17 -19
  42. data/lib/active_model/type/string.rb +12 -3
  43. data/lib/active_model/type/time.rb +1 -6
  44. data/lib/active_model/type/value.rb +9 -2
  45. data/lib/active_model/type.rb +3 -2
  46. data/lib/active_model/validations/absence.rb +2 -2
  47. data/lib/active_model/validations/acceptance.rb +34 -27
  48. data/lib/active_model/validations/callbacks.rb +15 -16
  49. data/lib/active_model/validations/clusivity.rb +6 -3
  50. data/lib/active_model/validations/confirmation.rb +4 -4
  51. data/lib/active_model/validations/exclusion.rb +1 -1
  52. data/lib/active_model/validations/format.rb +2 -3
  53. data/lib/active_model/validations/inclusion.rb +2 -2
  54. data/lib/active_model/validations/length.rb +3 -3
  55. data/lib/active_model/validations/numericality.rb +58 -44
  56. data/lib/active_model/validations/presence.rb +1 -1
  57. data/lib/active_model/validations/validates.rb +7 -6
  58. data/lib/active_model/validations.rb +6 -9
  59. data/lib/active_model/validator.rb +8 -3
  60. data/lib/active_model.rb +2 -1
  61. metadata +13 -7
@@ -11,10 +11,10 @@ module ActiveModel
11
11
  value = apply_seconds_precision(value)
12
12
 
13
13
  if value.acts_like?(:time)
14
- zone_conversion_method = is_utc? ? :getutc : :getlocal
15
-
16
- if value.respond_to?(zone_conversion_method)
17
- value = value.send(zone_conversion_method)
14
+ if is_utc?
15
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
16
+ else
17
+ value = value.getlocal if value.respond_to?(:getlocal)
18
18
  end
19
19
  end
20
20
 
@@ -22,10 +22,17 @@ module ActiveModel
22
22
  end
23
23
 
24
24
  def apply_seconds_precision(value)
25
- return value unless precision && value.respond_to?(:usec)
26
- number_of_insignificant_digits = 6 - precision
25
+ return value unless precision && value.respond_to?(:nsec)
26
+
27
+ number_of_insignificant_digits = 9 - precision
27
28
  round_power = 10**number_of_insignificant_digits
28
- value.change(usec: value.usec - value.usec % round_power)
29
+ rounded_off_nsec = value.nsec % round_power
30
+
31
+ if rounded_off_nsec > 0
32
+ value.change(nsec: value.nsec - rounded_off_nsec)
33
+ else
34
+ value
35
+ end
29
36
  end
30
37
 
31
38
  def type_cast_for_schema(value)
@@ -37,7 +44,6 @@ module ActiveModel
37
44
  end
38
45
 
39
46
  private
40
-
41
47
  def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
42
48
  # Treat 0000-00-00 00:00:00 as nil.
43
49
  return if year.nil? || (year == 0 && mon == 0 && mday == 0)
@@ -46,21 +52,37 @@ module ActiveModel
46
52
  time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
47
53
  return unless time
48
54
 
49
- time -= offset
55
+ time -= offset unless offset == 0
50
56
  is_utc? ? time : time.getlocal
57
+ elsif is_utc?
58
+ ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
51
59
  else
52
- ::Time.public_send(default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
60
+ ::Time.local(year, mon, mday, hour, min, sec, microsec) rescue nil
53
61
  end
54
62
  end
55
63
 
56
- ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
64
+ ISO_DATETIME = /
65
+ \A
66
+ (\d{4})-(\d\d)-(\d\d)(?:T|\s) # 2020-06-20T
67
+ (\d\d):(\d\d):(\d\d)(?:\.(\d{1,6})\d*)? # 10:20:30.123456
68
+ (?:(Z(?=\z)|[+-]\d\d)(?::?(\d\d))?)? # +09:00
69
+ \z
70
+ /x
57
71
 
58
- # Doesn't handle time zones.
59
72
  def fast_string_to_time(string)
60
- if string =~ ISO_DATETIME
61
- microsec = ($7.to_r * 1_000_000).to_i
62
- new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
73
+ return unless ISO_DATETIME =~ string
74
+
75
+ usec = $7.to_i
76
+ usec_len = $7&.length
77
+ if usec_len&.< 6
78
+ usec *= 10**(6 - usec_len)
63
79
  end
80
+
81
+ if $8
82
+ offset = $8 == "Z" ? 0 : $8.to_i * 3600 + $9.to_i * 60
83
+ end
84
+
85
+ new_time($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, usec, offset)
64
86
  end
65
87
  end
66
88
  end
@@ -7,7 +7,7 @@ module ActiveModel
7
7
  module Helpers # :nodoc: all
8
8
  module Timezone
9
9
  def is_utc?
10
- ::Time.zone_default.nil? || ::Time.zone_default =~ "UTC"
10
+ ::Time.zone_default.nil? || ::Time.zone_default.match?("UTC")
11
11
  end
12
12
 
13
13
  def default_timezone
@@ -3,29 +3,32 @@
3
3
  module ActiveModel
4
4
  module Type
5
5
  class ImmutableString < Value # :nodoc:
6
+ def initialize(**args)
7
+ @true = -(args.delete(:true)&.to_s || "t")
8
+ @false = -(args.delete(:false)&.to_s || "f")
9
+ super
10
+ end
11
+
6
12
  def type
7
13
  :string
8
14
  end
9
15
 
10
16
  def serialize(value)
11
17
  case value
12
- when ::Numeric, ActiveSupport::Duration then value.to_s
13
- when true then "t"
14
- when false then "f"
18
+ when ::Numeric, ::Symbol, ActiveSupport::Duration then value.to_s
19
+ when true then @true
20
+ when false then @false
15
21
  else super
16
22
  end
17
23
  end
18
24
 
19
25
  private
20
-
21
26
  def cast_value(value)
22
- result = \
23
- case value
24
- when true then "t"
25
- when false then "f"
26
- else value.to_s
27
- end
28
- result.freeze
27
+ case value
28
+ when true then @true
29
+ when false then @false
30
+ else value.to_s.freeze
31
+ end
29
32
  end
30
33
  end
31
34
  end
@@ -9,7 +9,7 @@ module ActiveModel
9
9
  # 4 bytes means an integer as opposed to smallint etc.
10
10
  DEFAULT_LIMIT = 4
11
11
 
12
- def initialize(*)
12
+ def initialize(**)
13
13
  super
14
14
  @range = min_value...max_value
15
15
  end
@@ -19,39 +19,36 @@ module ActiveModel
19
19
  end
20
20
 
21
21
  def deserialize(value)
22
- return if value.nil?
22
+ return if value.blank?
23
23
  value.to_i
24
24
  end
25
25
 
26
26
  def serialize(value)
27
- result = cast(value)
28
- if result
29
- ensure_in_range(result)
30
- end
31
- result
27
+ return if value.is_a?(::String) && non_numeric_string?(value)
28
+ ensure_in_range(super)
32
29
  end
33
30
 
34
- # TODO Change this to private once we've dropped Ruby 2.2 support.
35
- # Workaround for Ruby 2.2 "private attribute?" warning.
36
- protected
31
+ def serializable?(value)
32
+ cast_value = cast(value)
33
+ in_range?(cast_value) && super
34
+ end
37
35
 
36
+ private
38
37
  attr_reader :range
39
38
 
40
- private
39
+ def in_range?(value)
40
+ !value || range.member?(value)
41
+ end
41
42
 
42
43
  def cast_value(value)
43
- case value
44
- when true then 1
45
- when false then 0
46
- else
47
- value.to_i rescue nil
48
- end
44
+ value.to_i rescue nil
49
45
  end
50
46
 
51
47
  def ensure_in_range(value)
52
- unless range.cover?(value)
48
+ unless in_range?(value)
53
49
  raise ActiveModel::RangeError, "#{value} is out of range for #{self.class} with limit #{_limit} bytes"
54
50
  end
51
+ value
55
52
  end
56
53
 
57
54
  def max_value
@@ -8,8 +8,16 @@ module ActiveModel
8
8
  @registrations = []
9
9
  end
10
10
 
11
+ def initialize_dup(other)
12
+ @registrations = @registrations.dup
13
+ super
14
+ end
15
+
11
16
  def register(type_name, klass = nil, **options, &block)
12
- block ||= proc { |_, *args| klass.new(*args) }
17
+ unless block_given?
18
+ block = proc { |_, *args| klass.new(*args) }
19
+ block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
20
+ end
13
21
  registrations << registration_klass.new(type_name, block, **options)
14
22
  end
15
23
 
@@ -22,21 +30,17 @@ module ActiveModel
22
30
  raise ArgumentError, "Unknown type #{symbol.inspect}"
23
31
  end
24
32
  end
25
-
26
- # TODO Change this to private once we've dropped Ruby 2.2 support.
27
- # Workaround for Ruby 2.2 "private attribute?" warning.
28
- protected
29
-
30
- attr_reader :registrations
33
+ ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
31
34
 
32
35
  private
36
+ attr_reader :registrations
33
37
 
34
38
  def registration_klass
35
39
  Registration
36
40
  end
37
41
 
38
- def find_registration(symbol, *args)
39
- registrations.find { |r| r.matches?(symbol, *args) }
42
+ def find_registration(symbol, *args, **kwargs)
43
+ registrations.find { |r| r.matches?(symbol, *args, **kwargs) }
40
44
  end
41
45
  end
42
46
 
@@ -47,22 +51,16 @@ module ActiveModel
47
51
  @block = block
48
52
  end
49
53
 
50
- def call(_registry, *args, **kwargs)
51
- if kwargs.any? # https://bugs.ruby-lang.org/issues/10856
52
- block.call(*args, **kwargs)
53
- else
54
- block.call(*args)
55
- end
54
+ def call(_registry, *args)
55
+ block.call(*args)
56
56
  end
57
+ ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
57
58
 
58
59
  def matches?(type_name, *args, **kwargs)
59
60
  type_name == name
60
61
  end
61
62
 
62
- # TODO Change this to private once we've dropped Ruby 2.2 support.
63
- # Workaround for Ruby 2.2 "private attribute?" warning.
64
- protected
65
-
63
+ private
66
64
  attr_reader :name, :block
67
65
  end
68
66
  end
@@ -11,13 +11,22 @@ module ActiveModel
11
11
  end
12
12
  end
13
13
 
14
- private
14
+ def to_immutable_string
15
+ ImmutableString.new(
16
+ true: @true,
17
+ false: @false,
18
+ limit: limit,
19
+ precision: precision,
20
+ scale: scale,
21
+ )
22
+ end
15
23
 
24
+ private
16
25
  def cast_value(value)
17
26
  case value
18
27
  when ::String then ::String.new(value)
19
- when true then "t".freeze
20
- when false then "f".freeze
28
+ when true then @true
29
+ when false then @false
21
30
  else value.to_s
22
31
  end
23
32
  end
@@ -6,17 +6,13 @@ module ActiveModel
6
6
  include Helpers::Timezone
7
7
  include Helpers::TimeValue
8
8
  include Helpers::AcceptsMultiparameterTime.new(
9
- defaults: { 1 => 1970, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
9
+ defaults: { 1 => 2000, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
10
10
  )
11
11
 
12
12
  def type
13
13
  :time
14
14
  end
15
15
 
16
- def serialize(value)
17
- super(cast(value))
18
- end
19
-
20
16
  def user_input_in_time_zone(value)
21
17
  return unless value.present?
22
18
 
@@ -33,7 +29,6 @@ module ActiveModel
33
29
  end
34
30
 
35
31
  private
36
-
37
32
  def cast_value(value)
38
33
  return apply_seconds_precision(value) unless value.is_a?(::String)
39
34
  return if value.empty?
@@ -11,6 +11,14 @@ module ActiveModel
11
11
  @limit = limit
12
12
  end
13
13
 
14
+ # Returns true if this type can convert +value+ to a type that is usable
15
+ # by the database. For example a boolean type can return +true+ if the
16
+ # value parameter is a Ruby boolean, but may return +false+ if the value
17
+ # parameter is some other object.
18
+ def serializable?(value)
19
+ true
20
+ end
21
+
14
22
  def type # :nodoc:
15
23
  end
16
24
 
@@ -110,11 +118,10 @@ module ActiveModel
110
118
  [self.class, precision, scale, limit].hash
111
119
  end
112
120
 
113
- def assert_valid_value(*)
121
+ def assert_valid_value(_)
114
122
  end
115
123
 
116
124
  private
117
-
118
125
  # Convenience method for types which do not need separate type casting
119
126
  # behavior for user and database inputs. Called by Value#cast for
120
127
  # values except +nil+.
@@ -29,9 +29,10 @@ module ActiveModel
29
29
  registry.register(type_name, klass, **options, &block)
30
30
  end
31
31
 
32
- def lookup(*args, **kwargs) # :nodoc:
33
- registry.lookup(*args, **kwargs)
32
+ def lookup(*args) # :nodoc:
33
+ registry.lookup(*args)
34
34
  end
35
+ ruby2_keywords(:lookup) if respond_to?(:ruby2_keywords, true)
35
36
 
36
37
  def default_value # :nodoc:
37
38
  @default_value ||= Value.new
@@ -5,13 +5,13 @@ module ActiveModel
5
5
  # == \Active \Model Absence Validator
6
6
  class AbsenceValidator < EachValidator #:nodoc:
7
7
  def validate_each(record, attr_name, value)
8
- record.errors.add(attr_name, :present, options) if value.present?
8
+ record.errors.add(attr_name, :present, **options) if value.present?
9
9
  end
10
10
  end
11
11
 
12
12
  module HelperMethods
13
13
  # Validates that the specified attributes are blank (as defined by
14
- # Object#blank?). Happens by default on save.
14
+ # Object#present?). Happens by default on save.
15
15
  #
16
16
  # class Person < ActiveRecord::Base
17
17
  # validates_absence_of :first_name
@@ -10,14 +10,14 @@ module ActiveModel
10
10
 
11
11
  def validate_each(record, attribute, value)
12
12
  unless acceptable_option?(value)
13
- record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
13
+ record.errors.add(attribute, :accepted, **options.except(:accept, :allow_nil))
14
14
  end
15
15
  end
16
16
 
17
17
  private
18
-
19
18
  def setup!(klass)
20
- klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
19
+ define_attributes = LazilyDefineAttributes.new(attributes)
20
+ klass.include(define_attributes) unless klass.included_modules.include?(define_attributes)
21
21
  end
22
22
 
23
23
  def acceptable_option?(value)
@@ -25,50 +25,57 @@ module ActiveModel
25
25
  end
26
26
 
27
27
  class LazilyDefineAttributes < Module
28
- def initialize(attribute_definition)
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
+
29
36
  define_method(:respond_to_missing?) do |method_name, include_private = false|
30
- super(method_name, include_private) || attribute_definition.matches?(method_name)
37
+ mod.define_on(klass)
38
+ super(method_name, include_private) || mod.matches?(method_name)
31
39
  end
32
40
 
33
41
  define_method(:method_missing) do |method_name, *args, &block|
34
- if attribute_definition.matches?(method_name)
35
- attribute_definition.define_on(self.class)
42
+ mod.define_on(klass)
43
+ if mod.matches?(method_name)
36
44
  send(method_name, *args, &block)
37
45
  else
38
46
  super(method_name, *args, &block)
39
47
  end
40
48
  end
41
49
  end
42
- end
43
-
44
- class AttributeDefinition
45
- def initialize(attributes)
46
- @attributes = attributes.map(&:to_s)
47
- end
48
50
 
49
51
  def matches?(method_name)
50
- attr_name = convert_to_reader_name(method_name)
51
- attributes.include?(attr_name)
52
+ attr_name = method_name.to_s.chomp("=")
53
+ attributes.any? { |name| name == attr_name }
52
54
  end
53
55
 
54
56
  def define_on(klass)
55
- attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
56
- attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
57
- klass.send(:attr_reader, *attr_readers)
58
- klass.send(:attr_writer, *attr_writers)
59
- end
57
+ @lock&.synchronize do
58
+ return unless @lock
60
59
 
61
- # TODO Change this to private once we've dropped Ruby 2.2 support.
62
- # Workaround for Ruby 2.2 "private attribute?" warning.
63
- protected
60
+ attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
61
+ attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
64
62
 
65
- attr_reader :attributes
63
+ attr_reader(*attr_readers)
64
+ attr_writer(*attr_writers)
66
65
 
67
- private
66
+ remove_method :respond_to_missing?
67
+ remove_method :method_missing
68
68
 
69
- def convert_to_reader_name(method_name)
70
- method_name.to_s.chomp("=")
69
+ @lock = nil
71
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
72
79
  end
73
80
  end
74
81
 
@@ -56,14 +56,7 @@ module ActiveModel
56
56
  def before_validation(*args, &block)
57
57
  options = args.extract_options!
58
58
 
59
- if options.key?(:on)
60
- options = options.dup
61
- options[:on] = Array(options[:on])
62
- options[:if] = Array(options[:if])
63
- options[:if].unshift ->(o) {
64
- !(options[:on] & Array(o.validation_context)).empty?
65
- }
66
- end
59
+ set_options_for_callback(options)
67
60
 
68
61
  set_callback(:validation, :before, *args, options, &block)
69
62
  end
@@ -99,20 +92,26 @@ module ActiveModel
99
92
  options = options.dup
100
93
  options[:prepend] = true
101
94
 
102
- if options.key?(:on)
103
- options[:on] = Array(options[:on])
104
- options[:if] = Array(options[:if])
105
- options[:if].unshift ->(o) {
106
- !(options[:on] & Array(o.validation_context)).empty?
107
- }
108
- end
95
+ set_options_for_callback(options)
109
96
 
110
97
  set_callback(:validation, :after, *args, options, &block)
111
98
  end
99
+
100
+ private
101
+ def set_options_for_callback(options)
102
+ if options.key?(:on)
103
+ options[:on] = Array(options[:on])
104
+ options[:if] = [
105
+ ->(o) {
106
+ !(options[:on] & Array(o.validation_context)).empty?
107
+ },
108
+ *options[:if]
109
+ ]
110
+ end
111
+ end
112
112
  end
113
113
 
114
114
  private
115
-
116
115
  # Overwrite run validations to include callbacks.
117
116
  def run_validations!
118
117
  _run_validation_callbacks { super }
@@ -15,7 +15,6 @@ module ActiveModel
15
15
  end
16
16
 
17
17
  private
18
-
19
18
  def include?(record, value)
20
19
  members = if delimiter.respond_to?(:call)
21
20
  delimiter.call(record)
@@ -25,14 +24,18 @@ module ActiveModel
25
24
  delimiter
26
25
  end
27
26
 
28
- 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
29
32
  end
30
33
 
31
34
  def delimiter
32
35
  @delimiter ||= options[:in] || options[:within]
33
36
  end
34
37
 
35
- # In Ruby 2.2 <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
36
39
  # possible values in the range for equality, which is slower but more accurate.
37
40
  # <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
38
41
  # endpoints, which is fast but is only accurate on Numeric, Time, Date,
@@ -9,21 +9,21 @@ module ActiveModel
9
9
  end
10
10
 
11
11
  def validate_each(record, attribute, value)
12
- unless (confirmed = record.send("#{attribute}_confirmation")).nil?
12
+ unless (confirmed = record.public_send("#{attribute}_confirmation")).nil?
13
13
  unless confirmation_value_equal?(record, attribute, value, confirmed)
14
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))
15
+ record.errors.add(:"#{attribute}_confirmation", :confirmation, **options.except(:case_sensitive).merge!(attribute: human_attribute_name))
16
16
  end
17
17
  end
18
18
  end
19
19
 
20
20
  private
21
21
  def setup!(klass)
22
- klass.send(:attr_reader, *attributes.map do |attribute|
22
+ klass.attr_reader(*attributes.map do |attribute|
23
23
  :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
24
24
  end.compact)
25
25
 
26
- klass.send(:attr_writer, *attributes.map do |attribute|
26
+ klass.attr_writer(*attributes.map do |attribute|
27
27
  :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
28
28
  end.compact)
29
29
  end
@@ -9,7 +9,7 @@ module ActiveModel
9
9
 
10
10
  def validate_each(record, attribute, value)
11
11
  if include?(record, value)
12
- 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))
13
13
  end
14
14
  end
15
15
  end
@@ -6,7 +6,7 @@ module ActiveModel
6
6
  def validate_each(record, attribute, value)
7
7
  if options[:with]
8
8
  regexp = option_call(record, :with)
9
- record_error(record, attribute, :with, value) if value.to_s !~ regexp
9
+ record_error(record, attribute, :with, value) unless regexp.match?(value.to_s)
10
10
  elsif options[:without]
11
11
  regexp = option_call(record, :without)
12
12
  record_error(record, attribute, :without, value) if regexp.match?(value.to_s)
@@ -23,14 +23,13 @@ module ActiveModel
23
23
  end
24
24
 
25
25
  private
26
-
27
26
  def option_call(record, name)
28
27
  option = options[name]
29
28
  option.respond_to?(:call) ? option.call(record) : option
30
29
  end
31
30
 
32
31
  def record_error(record, attribute, name, value)
33
- record.errors.add(attribute, :invalid, options.except(name).merge!(value: value))
32
+ record.errors.add(attribute, :invalid, **options.except(name).merge!(value: value))
34
33
  end
35
34
 
36
35
  def check_options_validity(name)
@@ -9,7 +9,7 @@ module ActiveModel
9
9
 
10
10
  def validate_each(record, attribute, value)
11
11
  unless include?(record, value)
12
- 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))
13
13
  end
14
14
  end
15
15
  end
@@ -19,7 +19,7 @@ module ActiveModel
19
19
  # particular enumerable object.
20
20
  #
21
21
  # class Person < ActiveRecord::Base
22
- # validates_inclusion_of :gender, in: %w( m f )
22
+ # validates_inclusion_of :role, in: %w( admin contributor )
23
23
  # validates_inclusion_of :age, in: 0..99
24
24
  # validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
25
25
  # validates_inclusion_of :states, in: ->(person) { STATES[person.country] }