activemodel 6.0.6.1 → 6.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -272
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_model/attribute.rb +15 -14
  6. data/lib/active_model/attribute_assignment.rb +3 -4
  7. data/lib/active_model/attribute_methods.rb +74 -38
  8. data/lib/active_model/attribute_mutation_tracker.rb +8 -5
  9. data/lib/active_model/attribute_set/builder.rb +80 -13
  10. data/lib/active_model/attribute_set.rb +18 -16
  11. data/lib/active_model/attributes.rb +20 -24
  12. data/lib/active_model/dirty.rb +12 -4
  13. data/lib/active_model/error.rb +207 -0
  14. data/lib/active_model/errors.rb +316 -208
  15. data/lib/active_model/gem_version.rb +3 -3
  16. data/lib/active_model/lint.rb +1 -1
  17. data/lib/active_model/naming.rb +2 -2
  18. data/lib/active_model/nested_error.rb +22 -0
  19. data/lib/active_model/railtie.rb +1 -1
  20. data/lib/active_model/secure_password.rb +14 -14
  21. data/lib/active_model/serialization.rb +9 -6
  22. data/lib/active_model/serializers/json.rb +7 -0
  23. data/lib/active_model/type/date_time.rb +2 -2
  24. data/lib/active_model/type/float.rb +2 -0
  25. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +11 -7
  26. data/lib/active_model/type/helpers/numeric.rb +8 -3
  27. data/lib/active_model/type/helpers/time_value.rb +27 -17
  28. data/lib/active_model/type/helpers/timezone.rb +1 -1
  29. data/lib/active_model/type/immutable_string.rb +14 -10
  30. data/lib/active_model/type/integer.rb +11 -2
  31. data/lib/active_model/type/registry.rb +5 -0
  32. data/lib/active_model/type/string.rb +12 -2
  33. data/lib/active_model/type/value.rb +9 -1
  34. data/lib/active_model/validations/absence.rb +1 -1
  35. data/lib/active_model/validations/acceptance.rb +1 -1
  36. data/lib/active_model/validations/clusivity.rb +5 -1
  37. data/lib/active_model/validations/confirmation.rb +2 -2
  38. data/lib/active_model/validations/exclusion.rb +1 -1
  39. data/lib/active_model/validations/format.rb +2 -2
  40. data/lib/active_model/validations/inclusion.rb +1 -1
  41. data/lib/active_model/validations/length.rb +2 -2
  42. data/lib/active_model/validations/numericality.rb +48 -41
  43. data/lib/active_model/validations/presence.rb +1 -1
  44. data/lib/active_model/validations/validates.rb +6 -4
  45. data/lib/active_model/validations.rb +2 -2
  46. data/lib/active_model/validator.rb +7 -2
  47. data/lib/active_model.rb +2 -1
  48. metadata +15 -14
@@ -24,44 +24,24 @@ module ActiveModel
24
24
  end
25
25
  end
26
26
 
27
- def validate_each(record, attr_name, value)
28
- came_from_user = :"#{attr_name}_came_from_user?"
29
-
30
- if record.respond_to?(came_from_user)
31
- if record.public_send(came_from_user)
32
- raw_value = record.read_attribute_before_type_cast(attr_name)
33
- elsif record.respond_to?(:read_attribute)
34
- raw_value = record.read_attribute(attr_name)
35
- end
36
- else
37
- before_type_cast = :"#{attr_name}_before_type_cast"
38
- if record.respond_to?(before_type_cast)
39
- raw_value = record.public_send(before_type_cast)
40
- end
41
- end
42
- raw_value ||= value
43
-
44
- if record_attribute_changed_in_place?(record, attr_name)
45
- raw_value = value
46
- end
47
-
48
- unless is_number?(raw_value)
49
- record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
27
+ def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
28
+ unless is_number?(value, precision, scale)
29
+ record.errors.add(attr_name, :not_a_number, **filtered_options(value))
50
30
  return
51
31
  end
52
32
 
53
- if allow_only_integer?(record) && !is_integer?(raw_value)
54
- record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
33
+ if allow_only_integer?(record) && !is_integer?(value)
34
+ record.errors.add(attr_name, :not_an_integer, **filtered_options(value))
55
35
  return
56
36
  end
57
37
 
58
- value = parse_as_number(raw_value)
38
+ value = parse_as_number(value, precision, scale)
59
39
 
60
40
  options.slice(*CHECKS.keys).each do |option, option_value|
61
41
  case option
62
42
  when :odd, :even
63
- unless value.to_i.send(CHECKS[option])
64
- record.errors.add(attr_name, option, filtered_options(value))
43
+ unless value.to_i.public_send(CHECKS[option])
44
+ record.errors.add(attr_name, option, **filtered_options(value))
65
45
  end
66
46
  else
67
47
  case option_value
@@ -71,34 +51,38 @@ module ActiveModel
71
51
  option_value = record.send(option_value)
72
52
  end
73
53
 
74
- option_value = parse_as_number(option_value)
54
+ option_value = parse_as_number(option_value, precision, scale)
75
55
 
76
- unless value.send(CHECKS[option], option_value)
77
- record.errors.add(attr_name, option, filtered_options(value).merge!(count: option_value))
56
+ unless value.public_send(CHECKS[option], option_value)
57
+ record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
78
58
  end
79
59
  end
80
60
  end
81
61
  end
82
62
 
83
63
  private
84
- def is_number?(raw_value)
85
- !parse_as_number(raw_value).nil?
86
- rescue ArgumentError, TypeError
87
- false
88
- end
89
-
90
- def parse_as_number(raw_value)
64
+ def parse_as_number(raw_value, precision, scale)
91
65
  if raw_value.is_a?(Float)
92
- raw_value.to_d(Float::DIG)
66
+ parse_float(raw_value, precision, scale)
93
67
  elsif raw_value.is_a?(Numeric)
94
68
  raw_value
95
69
  elsif is_integer?(raw_value)
96
70
  raw_value.to_i
97
71
  elsif !is_hexadecimal_literal?(raw_value)
98
- Kernel.Float(raw_value).to_d
72
+ parse_float(Kernel.Float(raw_value), precision, scale)
99
73
  end
100
74
  end
101
75
 
76
+ def parse_float(raw_value, precision, scale)
77
+ (scale ? raw_value.truncate(scale) : raw_value).to_d(precision)
78
+ end
79
+
80
+ def is_number?(raw_value, precision, scale)
81
+ !parse_as_number(raw_value, precision, scale).nil?
82
+ rescue ArgumentError, TypeError
83
+ false
84
+ end
85
+
102
86
  def is_integer?(raw_value)
103
87
  INTEGER_REGEX.match?(raw_value.to_s)
104
88
  end
@@ -124,6 +108,27 @@ module ActiveModel
124
108
  end
125
109
  end
126
110
 
111
+ def read_attribute_for_validation(record, attr_name)
112
+ return super if record_attribute_changed_in_place?(record, attr_name)
113
+
114
+ came_from_user = :"#{attr_name}_came_from_user?"
115
+
116
+ if record.respond_to?(came_from_user)
117
+ if record.public_send(came_from_user)
118
+ raw_value = record.public_send(:"#{attr_name}_before_type_cast")
119
+ elsif record.respond_to?(:read_attribute)
120
+ raw_value = record.read_attribute(attr_name)
121
+ end
122
+ else
123
+ before_type_cast = :"#{attr_name}_before_type_cast"
124
+ if record.respond_to?(before_type_cast)
125
+ raw_value = record.public_send(before_type_cast)
126
+ end
127
+ end
128
+
129
+ raw_value || super
130
+ end
131
+
127
132
  def record_attribute_changed_in_place?(record, attr_name)
128
133
  record.respond_to?(:attribute_changed_in_place?) &&
129
134
  record.attribute_changed_in_place?(attr_name.to_s)
@@ -134,7 +139,8 @@ module ActiveModel
134
139
  # Validates whether the value of the specified attribute is numeric by
135
140
  # trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
136
141
  # is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\z/</tt>
137
- # (if <tt>only_integer</tt> is set to +true+).
142
+ # (if <tt>only_integer</tt> is set to +true+). Precision of Kernel.Float values
143
+ # are guaranteed up to 15 digits.
138
144
  #
139
145
  # class Person < ActiveRecord::Base
140
146
  # validates_numericality_of :value, on: :create
@@ -175,6 +181,7 @@ module ActiveModel
175
181
  # * <tt>:less_than</tt>
176
182
  # * <tt>:less_than_or_equal_to</tt>
177
183
  # * <tt>:only_integer</tt>
184
+ # * <tt>:other_than</tt>
178
185
  #
179
186
  # For example:
180
187
  #
@@ -4,7 +4,7 @@ module ActiveModel
4
4
  module Validations
5
5
  class PresenceValidator < EachValidator # :nodoc:
6
6
  def validate_each(record, attr_name, value)
7
- record.errors.add(attr_name, :blank, options) if value.blank?
7
+ record.errors.add(attr_name, :blank, **options) if value.blank?
8
8
  end
9
9
  end
10
10
 
@@ -12,6 +12,7 @@ module ActiveModel
12
12
  #
13
13
  # Examples of using the default rails validators:
14
14
  #
15
+ # validates :username, absence: true
15
16
  # validates :terms, acceptance: true
16
17
  # validates :password, confirmation: true
17
18
  # validates :username, exclusion: { in: %w(admin superuser) }
@@ -27,7 +28,7 @@ module ActiveModel
27
28
  # class EmailValidator < ActiveModel::EachValidator
28
29
  # def validate_each(record, attribute, value)
29
30
  # record.errors.add attribute, (options[:message] || "is not an email") unless
30
- # value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
31
+ # /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
31
32
  # end
32
33
  # end
33
34
  #
@@ -47,7 +48,7 @@ module ActiveModel
47
48
  #
48
49
  # class TitleValidator < ActiveModel::EachValidator
49
50
  # def validate_each(record, attribute, value)
50
- # record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
51
+ # record.errors.add attribute, "must start with 'the'" unless /\Athe/i.match?(value)
51
52
  # end
52
53
  # end
53
54
  #
@@ -63,7 +64,7 @@ module ActiveModel
63
64
  # and strings in shortcut form.
64
65
  #
65
66
  # validates :email, format: /@/
66
- # validates :role, inclusion: %(admin contributor)
67
+ # validates :role, inclusion: %w(admin contributor)
67
68
  # validates :password, length: 6..20
68
69
  #
69
70
  # When using shortcut form, ranges and arrays are passed to your
@@ -112,7 +113,6 @@ module ActiveModel
112
113
  defaults[:attributes] = attributes
113
114
 
114
115
  validations.each do |key, options|
115
- next unless options
116
116
  key = "#{key.to_s.camelize}Validator"
117
117
 
118
118
  begin
@@ -121,6 +121,8 @@ module ActiveModel
121
121
  raise ArgumentError, "Unknown validator: '#{key}'"
122
122
  end
123
123
 
124
+ next unless options
125
+
124
126
  validates_with(validator, defaults.merge(_parse_validates_options(options)))
125
127
  end
126
128
  end
@@ -15,7 +15,7 @@ module ActiveModel
15
15
  # attr_accessor :first_name, :last_name
16
16
  #
17
17
  # validates_each :first_name, :last_name do |record, attr, value|
18
- # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
18
+ # record.errors.add attr, "starts with z." if value.start_with?("z")
19
19
  # end
20
20
  # end
21
21
  #
@@ -61,7 +61,7 @@ module ActiveModel
61
61
  # attr_accessor :first_name, :last_name
62
62
  #
63
63
  # validates_each :first_name, :last_name, allow_blank: true do |record, attr, value|
64
- # record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
64
+ # record.errors.add attr, "starts with z." if value.start_with?("z")
65
65
  # end
66
66
  # end
67
67
  #
@@ -85,7 +85,7 @@ module ActiveModel
85
85
  #
86
86
  # It can be useful to access the class that is using that validator when there are prerequisites such
87
87
  # as an +attr_accessor+ being present. This class is accessible via <tt>options[:class]</tt> in the constructor.
88
- # To setup your validator override the constructor.
88
+ # To set up your validator override the constructor.
89
89
  #
90
90
  # class MyValidator < ActiveModel::Validator
91
91
  # def initialize(options={})
@@ -147,7 +147,7 @@ module ActiveModel
147
147
  # override +validate_each+ with validation logic.
148
148
  def validate(record)
149
149
  attributes.each do |attribute|
150
- value = record.read_attribute_for_validation(attribute)
150
+ value = read_attribute_for_validation(record, attribute)
151
151
  next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
152
152
  validate_each(record, attribute, value)
153
153
  end
@@ -164,6 +164,11 @@ module ActiveModel
164
164
  # +ArgumentError+ when invalid options are supplied.
165
165
  def check_validity!
166
166
  end
167
+
168
+ private
169
+ def read_attribute_for_validation(record, attr_name)
170
+ record.read_attribute_for_validation(attr_name)
171
+ end
167
172
  end
168
173
 
169
174
  # +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
data/lib/active_model.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2019 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2020 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -53,6 +53,7 @@ module ActiveModel
53
53
 
54
54
  eager_autoload do
55
55
  autoload :Errors
56
+ autoload :Error
56
57
  autoload :RangeError, "active_model/errors"
57
58
  autoload :StrictValidationFailed, "active_model/errors"
58
59
  autoload :UnknownAttributeError, "active_model/errors"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.6.1
4
+ version: 6.1.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-17 00:00:00.000000000 Z
11
+ date: 2020-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.6.1
19
+ version: 6.1.0.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 6.0.6.1
26
+ version: 6.1.0.rc1
27
27
  description: A toolkit for building modeling frameworks like Active Record. Rich support
28
28
  for attributes, callbacks, validations, serialization, internationalization, and
29
29
  testing.
@@ -48,6 +48,7 @@ files:
48
48
  - lib/active_model/callbacks.rb
49
49
  - lib/active_model/conversion.rb
50
50
  - lib/active_model/dirty.rb
51
+ - lib/active_model/error.rb
51
52
  - lib/active_model/errors.rb
52
53
  - lib/active_model/forbidden_attributes_protection.rb
53
54
  - lib/active_model/gem_version.rb
@@ -55,6 +56,7 @@ files:
55
56
  - lib/active_model/locale/en.yml
56
57
  - lib/active_model/model.rb
57
58
  - lib/active_model/naming.rb
59
+ - lib/active_model/nested_error.rb
58
60
  - lib/active_model/railtie.rb
59
61
  - lib/active_model/secure_password.rb
60
62
  - lib/active_model/serialization.rb
@@ -102,12 +104,11 @@ licenses:
102
104
  - MIT
103
105
  metadata:
104
106
  bug_tracker_uri: https://github.com/rails/rails/issues
105
- changelog_uri: https://github.com/rails/rails/blob/v6.0.6.1/activemodel/CHANGELOG.md
106
- documentation_uri: https://api.rubyonrails.org/v6.0.6.1/
107
+ changelog_uri: https://github.com/rails/rails/blob/v6.1.0.rc1/activemodel/CHANGELOG.md
108
+ documentation_uri: https://api.rubyonrails.org/v6.1.0.rc1/
107
109
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
108
- source_code_uri: https://github.com/rails/rails/tree/v6.0.6.1/activemodel
109
- rubygems_mfa_required: 'true'
110
- post_install_message:
110
+ source_code_uri: https://github.com/rails/rails/tree/v6.1.0.rc1/activemodel
111
+ post_install_message:
111
112
  rdoc_options: []
112
113
  require_paths:
113
114
  - lib
@@ -118,12 +119,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
118
119
  version: 2.5.0
119
120
  required_rubygems_version: !ruby/object:Gem::Requirement
120
121
  requirements:
121
- - - ">="
122
+ - - ">"
122
123
  - !ruby/object:Gem::Version
123
- version: '0'
124
+ version: 1.3.1
124
125
  requirements: []
125
- rubygems_version: 3.4.3
126
- signing_key:
126
+ rubygems_version: 3.1.4
127
+ signing_key:
127
128
  specification_version: 4
128
129
  summary: A toolkit for building modeling frameworks (part of Rails).
129
130
  test_files: []