activemodel 6.1.6.1 → 7.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -164
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_model/api.rb +99 -0
  6. data/lib/active_model/attribute.rb +4 -0
  7. data/lib/active_model/attribute_methods.rb +99 -52
  8. data/lib/active_model/attribute_set.rb +4 -1
  9. data/lib/active_model/attributes.rb +15 -12
  10. data/lib/active_model/callbacks.rb +1 -1
  11. data/lib/active_model/conversion.rb +2 -2
  12. data/lib/active_model/dirty.rb +5 -4
  13. data/lib/active_model/errors.rb +17 -3
  14. data/lib/active_model/gem_version.rb +4 -4
  15. data/lib/active_model/locale/en.yml +1 -0
  16. data/lib/active_model/model.rb +6 -59
  17. data/lib/active_model/naming.rb +15 -8
  18. data/lib/active_model/secure_password.rb +0 -1
  19. data/lib/active_model/serialization.rb +7 -2
  20. data/lib/active_model/translation.rb +1 -1
  21. data/lib/active_model/type/helpers/numeric.rb +9 -1
  22. data/lib/active_model/type/helpers/time_value.rb +2 -2
  23. data/lib/active_model/type/integer.rb +4 -1
  24. data/lib/active_model/type/registry.rb +8 -38
  25. data/lib/active_model/type/time.rb +1 -1
  26. data/lib/active_model/type.rb +6 -6
  27. data/lib/active_model/validations/absence.rb +1 -1
  28. data/lib/active_model/validations/clusivity.rb +1 -1
  29. data/lib/active_model/validations/comparability.rb +29 -0
  30. data/lib/active_model/validations/comparison.rb +82 -0
  31. data/lib/active_model/validations/confirmation.rb +4 -4
  32. data/lib/active_model/validations/numericality.rb +27 -20
  33. data/lib/active_model/validations.rb +4 -4
  34. data/lib/active_model/validator.rb +2 -2
  35. data/lib/active_model.rb +2 -1
  36. metadata +17 -15
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/validations/comparability"
4
+
5
+ module ActiveModel
6
+ module Validations
7
+ class ComparisonValidator < EachValidator # :nodoc:
8
+ include Comparability
9
+
10
+ def check_validity!
11
+ unless (options.keys & COMPARE_CHECKS.keys).any?
12
+ raise ArgumentError, "Expected one of :greater_than, :greater_than_or_equal_to, "\
13
+ ":equal_to, :less_than, :less_than_or_equal_to, or :other_than option to be supplied."
14
+ end
15
+ end
16
+
17
+ def validate_each(record, attr_name, value)
18
+ options.slice(*COMPARE_CHECKS.keys).each do |option, raw_option_value|
19
+ option_value = option_value(record, raw_option_value)
20
+
21
+ if value.nil? || value.blank?
22
+ return record.errors.add(attr_name, :blank, **error_options(value, option_value))
23
+ end
24
+
25
+ unless value.public_send(COMPARE_CHECKS[option], option_value)
26
+ record.errors.add(attr_name, option, **error_options(value, option_value))
27
+ end
28
+ rescue ArgumentError => e
29
+ record.errors.add(attr_name, e.message)
30
+ end
31
+ end
32
+ end
33
+
34
+ module HelperMethods
35
+ # Validates the value of a specified attribute fulfills all
36
+ # defined comparisons with another value, proc, or attribute.
37
+ #
38
+ # class Person < ActiveRecord::Base
39
+ # validates_comparison_of :value, greater_than: 'the sum of its parts'
40
+ # end
41
+ #
42
+ # Configuration options:
43
+ # * <tt>:message</tt> - A custom error message (default is: "failed comparison").
44
+ # * <tt>:greater_than</tt> - Specifies the value must be greater than the
45
+ # supplied value.
46
+ # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be
47
+ # greater than or equal to the supplied value.
48
+ # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied
49
+ # value.
50
+ # * <tt>:less_than</tt> - Specifies the value must be less than the
51
+ # supplied value.
52
+ # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less
53
+ # than or equal to the supplied value.
54
+ # * <tt>:other_than</tt> - Specifies the value must not be equal to the
55
+ # supplied value.
56
+ #
57
+ # There is also a list of default options supported by every validator:
58
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
59
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
60
+ #
61
+ # The validator requires at least one of the following checks to be supplied.
62
+ # Each will accept a proc, value, or a symbol which corresponds to a method:
63
+ #
64
+ # * <tt>:greater_than</tt>
65
+ # * <tt>:greater_than_or_equal_to</tt>
66
+ # * <tt>:equal_to</tt>
67
+ # * <tt>:less_than</tt>
68
+ # * <tt>:less_than_or_equal_to</tt>
69
+ # * <tt>:other_than</tt>
70
+ #
71
+ # For example:
72
+ #
73
+ # class Person < ActiveRecord::Base
74
+ # validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
75
+ # validates_comparison_of :preferred_name, other_than: :given_name, allow_nil: true
76
+ # end
77
+ def validates_comparison_of(*attr_names)
78
+ validates_with ComparisonValidator, _merge_attributes(attr_names)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -19,13 +19,13 @@ module ActiveModel
19
19
 
20
20
  private
21
21
  def setup!(klass)
22
- klass.attr_reader(*attributes.map do |attribute|
22
+ klass.attr_reader(*attributes.filter_map do |attribute|
23
23
  :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
24
- end.compact)
24
+ end)
25
25
 
26
- klass.attr_writer(*attributes.map do |attribute|
26
+ klass.attr_writer(*attributes.filter_map do |attribute|
27
27
  :"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
28
- end.compact)
28
+ end)
29
29
  end
30
30
 
31
31
  def confirmation_value_equal?(record, attribute, value, confirmed)
@@ -1,27 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_model/validations/comparability"
3
4
  require "bigdecimal/util"
4
5
 
5
6
  module ActiveModel
6
7
  module Validations
7
8
  class NumericalityValidator < EachValidator # :nodoc:
8
- CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
9
- equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
10
- odd: :odd?, even: :even?, other_than: :!= }.freeze
9
+ include Comparability
11
10
 
12
- RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
11
+ RANGE_CHECKS = { in: :in? }
12
+ NUMBER_CHECKS = { odd: :odd?, even: :even? }
13
+
14
+ RESERVED_OPTIONS = COMPARE_CHECKS.keys + NUMBER_CHECKS.keys + RANGE_CHECKS.keys + [:only_integer]
13
15
 
14
16
  INTEGER_REGEX = /\A[+-]?\d+\z/
15
17
 
16
18
  HEXADECIMAL_REGEX = /\A[+-]?0[xX]/
17
19
 
18
20
  def check_validity!
19
- keys = CHECKS.keys - [:odd, :even]
20
- options.slice(*keys).each do |option, value|
21
+ options.slice(*COMPARE_CHECKS.keys).each do |option, value|
21
22
  unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
22
23
  raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
23
24
  end
24
25
  end
26
+
27
+ options.slice(*RANGE_CHECKS.keys).each do |option, value|
28
+ unless value.is_a?(Range)
29
+ raise ArgumentError, ":#{option} must be a range"
30
+ end
31
+ end
25
32
  end
26
33
 
27
34
  def validate_each(record, attr_name, value, precision: Float::DIG, scale: nil)
@@ -37,23 +44,18 @@ module ActiveModel
37
44
 
38
45
  value = parse_as_number(value, precision, scale)
39
46
 
40
- options.slice(*CHECKS.keys).each do |option, option_value|
41
- case option
42
- when :odd, :even
43
- unless value.to_i.public_send(CHECKS[option])
47
+ options.slice(*RESERVED_OPTIONS).each do |option, option_value|
48
+ if NUMBER_CHECKS.include?(option)
49
+ unless value.to_i.public_send(NUMBER_CHECKS[option])
44
50
  record.errors.add(attr_name, option, **filtered_options(value))
45
51
  end
46
- else
47
- case option_value
48
- when Proc
49
- option_value = option_value.call(record)
50
- when Symbol
51
- option_value = record.send(option_value)
52
+ elsif RANGE_CHECKS.include?(option)
53
+ unless value.public_send(RANGE_CHECKS[option], option_value)
54
+ record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
52
55
  end
53
-
54
- option_value = parse_as_number(option_value, precision, scale)
55
-
56
- unless value.public_send(CHECKS[option], option_value)
56
+ elsif COMPARE_CHECKS.include?(option)
57
+ option_value = option_as_number(record, option_value, precision, scale)
58
+ unless value.public_send(COMPARE_CHECKS[option], option_value)
57
59
  record.errors.add(attr_name, option, **filtered_options(value).merge!(count: option_value))
58
60
  end
59
61
  end
@@ -61,6 +63,10 @@ module ActiveModel
61
63
  end
62
64
 
63
65
  private
66
+ def option_as_number(record, option_value, precision, scale)
67
+ parse_as_number(option_value(record, option_value), precision, scale)
68
+ end
69
+
64
70
  def parse_as_number(raw_value, precision, scale)
65
71
  if raw_value.is_a?(Float)
66
72
  parse_float(raw_value, precision, scale)
@@ -173,6 +179,7 @@ module ActiveModel
173
179
  # supplied value.
174
180
  # * <tt>:odd</tt> - Specifies the value must be an odd number.
175
181
  # * <tt>:even</tt> - Specifies the value must be an even number.
182
+ # * <tt>:in</tt> - Check that the value is within a range.
176
183
  #
177
184
  # There is also a list of default options supported by every validator:
178
185
  # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
@@ -118,7 +118,7 @@ module ActiveModel
118
118
  # end
119
119
  # end
120
120
  #
121
- # Or with a block where self points to the current record to be validated:
121
+ # Or with a block where +self+ points to the current record to be validated:
122
122
  #
123
123
  # class Comment
124
124
  # include ActiveModel::Validations
@@ -152,7 +152,7 @@ module ActiveModel
152
152
  def validate(*args, &block)
153
153
  options = args.extract_options!
154
154
 
155
- if args.all? { |arg| arg.is_a?(Symbol) }
155
+ if args.all?(Symbol)
156
156
  options.each_key do |k|
157
157
  unless VALID_OPTIONS_FOR_VALIDATE.include?(k)
158
158
  raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{VALID_OPTIONS_FOR_VALIDATE.map(&:inspect).join(', ')}. Perhaps you meant to call `validates` instead of `validate`?")
@@ -272,7 +272,7 @@ module ActiveModel
272
272
  end
273
273
 
274
274
  # Copy validators on inheritance.
275
- def inherited(base) #:nodoc:
275
+ def inherited(base) # :nodoc:
276
276
  dup = _validators.dup
277
277
  base._validators = dup.each { |k, v| dup[k] = v.dup }
278
278
  super
@@ -280,7 +280,7 @@ module ActiveModel
280
280
  end
281
281
 
282
282
  # Clean the +Errors+ object if instance is duped.
283
- def initialize_dup(other) #:nodoc:
283
+ def initialize_dup(other) # :nodoc:
284
284
  @errors = nil
285
285
  super
286
286
  end
@@ -129,7 +129,7 @@ module ActiveModel
129
129
  # record, attribute and value.
130
130
  #
131
131
  # All \Active \Model validations are built on top of this validator.
132
- class EachValidator < Validator #:nodoc:
132
+ class EachValidator < Validator
133
133
  attr_reader :attributes
134
134
 
135
135
  # Returns a new validator instance. All options will be available via the
@@ -174,7 +174,7 @@ module ActiveModel
174
174
 
175
175
  # +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
176
176
  # and call this block for each attribute being validated. +validates_each+ uses this validator.
177
- class BlockValidator < EachValidator #:nodoc:
177
+ class BlockValidator < EachValidator # :nodoc:
178
178
  def initialize(options, &block)
179
179
  @block = block
180
180
  super
data/lib/active_model.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2022 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2021 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
@@ -30,6 +30,7 @@ require "active_model/version"
30
30
  module ActiveModel
31
31
  extend ActiveSupport::Autoload
32
32
 
33
+ autoload :API
33
34
  autoload :Attribute
34
35
  autoload :Attributes
35
36
  autoload :AttributeAssignment
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.1.6.1
4
+ version: 7.0.0.alpha1
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: 2022-07-12 00:00:00.000000000 Z
11
+ date: 2021-09-15 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.1.6.1
19
+ version: 7.0.0.alpha1
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.1.6.1
26
+ version: 7.0.0.alpha1
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.
@@ -36,6 +36,7 @@ files:
36
36
  - MIT-LICENSE
37
37
  - README.rdoc
38
38
  - lib/active_model.rb
39
+ - lib/active_model/api.rb
39
40
  - lib/active_model/attribute.rb
40
41
  - lib/active_model/attribute/user_provided_default.rb
41
42
  - lib/active_model/attribute_assignment.rb
@@ -87,6 +88,8 @@ files:
87
88
  - lib/active_model/validations/acceptance.rb
88
89
  - lib/active_model/validations/callbacks.rb
89
90
  - lib/active_model/validations/clusivity.rb
91
+ - lib/active_model/validations/comparability.rb
92
+ - lib/active_model/validations/comparison.rb
90
93
  - lib/active_model/validations/confirmation.rb
91
94
  - lib/active_model/validations/exclusion.rb
92
95
  - lib/active_model/validations/format.rb
@@ -104,12 +107,11 @@ licenses:
104
107
  - MIT
105
108
  metadata:
106
109
  bug_tracker_uri: https://github.com/rails/rails/issues
107
- changelog_uri: https://github.com/rails/rails/blob/v6.1.6.1/activemodel/CHANGELOG.md
108
- documentation_uri: https://api.rubyonrails.org/v6.1.6.1/
110
+ changelog_uri: https://github.com/rails/rails/blob/v7.0.0.alpha1/activemodel/CHANGELOG.md
111
+ documentation_uri: https://api.rubyonrails.org/v7.0.0.alpha1/
109
112
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
110
- source_code_uri: https://github.com/rails/rails/tree/v6.1.6.1/activemodel
111
- rubygems_mfa_required: 'true'
112
- post_install_message:
113
+ source_code_uri: https://github.com/rails/rails/tree/v7.0.0.alpha1/activemodel
114
+ post_install_message:
113
115
  rdoc_options: []
114
116
  require_paths:
115
117
  - lib
@@ -117,15 +119,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
119
  requirements:
118
120
  - - ">="
119
121
  - !ruby/object:Gem::Version
120
- version: 2.5.0
122
+ version: 2.7.0
121
123
  required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  requirements:
123
- - - ">="
125
+ - - ">"
124
126
  - !ruby/object:Gem::Version
125
- version: '0'
127
+ version: 1.3.1
126
128
  requirements: []
127
- rubygems_version: 3.3.3
128
- signing_key:
129
+ rubygems_version: 3.1.6
130
+ signing_key:
129
131
  specification_version: 4
130
132
  summary: A toolkit for building modeling frameworks (part of Rails).
131
133
  test_files: []