activemodel 5.2.3

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +114 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +264 -0
  5. data/lib/active_model.rb +77 -0
  6. data/lib/active_model/attribute.rb +248 -0
  7. data/lib/active_model/attribute/user_provided_default.rb +52 -0
  8. data/lib/active_model/attribute_assignment.rb +57 -0
  9. data/lib/active_model/attribute_methods.rb +478 -0
  10. data/lib/active_model/attribute_mutation_tracker.rb +124 -0
  11. data/lib/active_model/attribute_set.rb +114 -0
  12. data/lib/active_model/attribute_set/builder.rb +126 -0
  13. data/lib/active_model/attribute_set/yaml_encoder.rb +41 -0
  14. data/lib/active_model/attributes.rb +111 -0
  15. data/lib/active_model/callbacks.rb +153 -0
  16. data/lib/active_model/conversion.rb +111 -0
  17. data/lib/active_model/dirty.rb +343 -0
  18. data/lib/active_model/errors.rb +517 -0
  19. data/lib/active_model/forbidden_attributes_protection.rb +31 -0
  20. data/lib/active_model/gem_version.rb +17 -0
  21. data/lib/active_model/lint.rb +118 -0
  22. data/lib/active_model/locale/en.yml +36 -0
  23. data/lib/active_model/model.rb +99 -0
  24. data/lib/active_model/naming.rb +318 -0
  25. data/lib/active_model/railtie.rb +14 -0
  26. data/lib/active_model/secure_password.rb +129 -0
  27. data/lib/active_model/serialization.rb +192 -0
  28. data/lib/active_model/serializers/json.rb +146 -0
  29. data/lib/active_model/translation.rb +70 -0
  30. data/lib/active_model/type.rb +53 -0
  31. data/lib/active_model/type/big_integer.rb +15 -0
  32. data/lib/active_model/type/binary.rb +52 -0
  33. data/lib/active_model/type/boolean.rb +38 -0
  34. data/lib/active_model/type/date.rb +57 -0
  35. data/lib/active_model/type/date_time.rb +51 -0
  36. data/lib/active_model/type/decimal.rb +70 -0
  37. data/lib/active_model/type/float.rb +36 -0
  38. data/lib/active_model/type/helpers.rb +7 -0
  39. data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +41 -0
  40. data/lib/active_model/type/helpers/mutable.rb +20 -0
  41. data/lib/active_model/type/helpers/numeric.rb +37 -0
  42. data/lib/active_model/type/helpers/time_value.rb +68 -0
  43. data/lib/active_model/type/helpers/timezone.rb +19 -0
  44. data/lib/active_model/type/immutable_string.rb +32 -0
  45. data/lib/active_model/type/integer.rb +70 -0
  46. data/lib/active_model/type/registry.rb +70 -0
  47. data/lib/active_model/type/string.rb +26 -0
  48. data/lib/active_model/type/time.rb +51 -0
  49. data/lib/active_model/type/value.rb +126 -0
  50. data/lib/active_model/validations.rb +439 -0
  51. data/lib/active_model/validations/absence.rb +33 -0
  52. data/lib/active_model/validations/acceptance.rb +106 -0
  53. data/lib/active_model/validations/callbacks.rb +122 -0
  54. data/lib/active_model/validations/clusivity.rb +54 -0
  55. data/lib/active_model/validations/confirmation.rb +80 -0
  56. data/lib/active_model/validations/exclusion.rb +49 -0
  57. data/lib/active_model/validations/format.rb +114 -0
  58. data/lib/active_model/validations/helper_methods.rb +15 -0
  59. data/lib/active_model/validations/inclusion.rb +47 -0
  60. data/lib/active_model/validations/length.rb +129 -0
  61. data/lib/active_model/validations/numericality.rb +189 -0
  62. data/lib/active_model/validations/presence.rb +39 -0
  63. data/lib/active_model/validations/validates.rb +174 -0
  64. data/lib/active_model/validations/with.rb +147 -0
  65. data/lib/active_model/validator.rb +183 -0
  66. data/lib/active_model/version.rb +10 -0
  67. metadata +125 -0
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Validations
5
+ class FormatValidator < EachValidator # :nodoc:
6
+ def validate_each(record, attribute, value)
7
+ if options[:with]
8
+ regexp = option_call(record, :with)
9
+ record_error(record, attribute, :with, value) if value.to_s !~ regexp
10
+ elsif options[:without]
11
+ regexp = option_call(record, :without)
12
+ record_error(record, attribute, :without, value) if regexp.match?(value.to_s)
13
+ end
14
+ end
15
+
16
+ def check_validity!
17
+ unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
18
+ raise ArgumentError, "Either :with or :without must be supplied (but not both)"
19
+ end
20
+
21
+ check_options_validity :with
22
+ check_options_validity :without
23
+ end
24
+
25
+ private
26
+
27
+ def option_call(record, name)
28
+ option = options[name]
29
+ option.respond_to?(:call) ? option.call(record) : option
30
+ end
31
+
32
+ def record_error(record, attribute, name, value)
33
+ record.errors.add(attribute, :invalid, options.except(name).merge!(value: value))
34
+ end
35
+
36
+ def check_options_validity(name)
37
+ if option = options[name]
38
+ if option.is_a?(Regexp)
39
+ if options[:multiline] != true && regexp_using_multiline_anchors?(option)
40
+ raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
41
+ "which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
42
+ ":multiline => true option?"
43
+ end
44
+ elsif !option.respond_to?(:call)
45
+ raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
46
+ end
47
+ end
48
+ end
49
+
50
+ def regexp_using_multiline_anchors?(regexp)
51
+ source = regexp.source
52
+ source.start_with?("^") || (source.end_with?("$") && !source.end_with?("\\$"))
53
+ end
54
+ end
55
+
56
+ module HelperMethods
57
+ # Validates whether the value of the specified attribute is of the correct
58
+ # form, going by the regular expression provided. You can require that the
59
+ # attribute matches the regular expression:
60
+ #
61
+ # class Person < ActiveRecord::Base
62
+ # validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create
63
+ # end
64
+ #
65
+ # Alternatively, you can require that the specified attribute does _not_
66
+ # match the regular expression:
67
+ #
68
+ # class Person < ActiveRecord::Base
69
+ # validates_format_of :email, without: /NOSPAM/
70
+ # end
71
+ #
72
+ # You can also provide a proc or lambda which will determine the regular
73
+ # expression that will be used to validate the attribute.
74
+ #
75
+ # class Person < ActiveRecord::Base
76
+ # # Admin can have number as a first letter in their screen name
77
+ # validates_format_of :screen_name,
78
+ # with: ->(person) { person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
79
+ # end
80
+ #
81
+ # Note: use <tt>\A</tt> and <tt>\z</tt> to match the start and end of the
82
+ # string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
83
+ #
84
+ # Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass
85
+ # the <tt>multiline: true</tt> option in case you use any of these two
86
+ # anchors in the provided regular expression. In most cases, you should be
87
+ # using <tt>\A</tt> and <tt>\z</tt>.
88
+ #
89
+ # You must pass either <tt>:with</tt> or <tt>:without</tt> as an option.
90
+ # In addition, both must be a regular expression or a proc or lambda, or
91
+ # else an exception will be raised.
92
+ #
93
+ # Configuration options:
94
+ # * <tt>:message</tt> - A custom error message (default is: "is invalid").
95
+ # * <tt>:with</tt> - Regular expression that if the attribute matches will
96
+ # result in a successful validation. This can be provided as a proc or
97
+ # lambda returning regular expression which will be called at runtime.
98
+ # * <tt>:without</tt> - Regular expression that if the attribute does not
99
+ # match will result in a successful validation. This can be provided as
100
+ # a proc or lambda returning regular expression which will be called at
101
+ # runtime.
102
+ # * <tt>:multiline</tt> - Set to true if your regular expression contains
103
+ # anchors that match the beginning or end of lines as opposed to the
104
+ # beginning or end of the string. These anchors are <tt>^</tt> and <tt>$</tt>.
105
+ #
106
+ # There is also a list of default options supported by every validator:
107
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
108
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
109
+ def validates_format_of(*attr_names)
110
+ validates_with FormatValidator, _merge_attributes(attr_names)
111
+ end
112
+ end
113
+ end
114
+ 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
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/validations/clusivity"
4
+
5
+ module ActiveModel
6
+ module Validations
7
+ class InclusionValidator < EachValidator # :nodoc:
8
+ include Clusivity
9
+
10
+ def validate_each(record, attribute, value)
11
+ unless include?(record, value)
12
+ record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(value: value))
13
+ end
14
+ end
15
+ end
16
+
17
+ module HelperMethods
18
+ # Validates whether the value of the specified attribute is available in a
19
+ # particular enumerable object.
20
+ #
21
+ # class Person < ActiveRecord::Base
22
+ # validates_inclusion_of :gender, in: %w( m f )
23
+ # validates_inclusion_of :age, in: 0..99
24
+ # validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
25
+ # validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
26
+ # validates_inclusion_of :karma, in: :available_karmas
27
+ # end
28
+ #
29
+ # Configuration options:
30
+ # * <tt>:in</tt> - An enumerable object of available items. This can be
31
+ # supplied as a proc, lambda or symbol which returns an enumerable. If the
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.
35
+ # * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
36
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is
37
+ # not included in the list").
38
+ #
39
+ # There is also a list of default options supported by every validator:
40
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
41
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
42
+ def validates_inclusion_of(*attr_names)
43
+ validates_with InclusionValidator, _merge_attributes(attr_names)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ module Validations
5
+ class LengthValidator < EachValidator # :nodoc:
6
+ MESSAGES = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
7
+ CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
8
+
9
+ RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :too_short, :too_long]
10
+
11
+ def initialize(options)
12
+ if range = (options.delete(:in) || options.delete(:within))
13
+ raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
14
+ options[:minimum], options[:maximum] = range.min, range.max
15
+ end
16
+
17
+ if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
18
+ options[:minimum] = 1
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ def check_validity!
25
+ keys = CHECKS.keys & options.keys
26
+
27
+ if keys.empty?
28
+ raise ArgumentError, "Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option."
29
+ end
30
+
31
+ keys.each do |key|
32
+ value = options[key]
33
+
34
+ unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY || value.is_a?(Symbol) || value.is_a?(Proc)
35
+ raise ArgumentError, ":#{key} must be a nonnegative Integer, Infinity, Symbol, or Proc"
36
+ end
37
+ end
38
+ end
39
+
40
+ def validate_each(record, attribute, value)
41
+ value_length = value.respond_to?(:length) ? value.length : value.to_s.length
42
+ errors_options = options.except(*RESERVED_OPTIONS)
43
+
44
+ CHECKS.each do |key, validity_check|
45
+ next unless check_value = options[key]
46
+
47
+ if !value.nil? || skip_nil_check?(key)
48
+ case check_value
49
+ when Proc
50
+ check_value = check_value.call(record)
51
+ when Symbol
52
+ check_value = record.send(check_value)
53
+ end
54
+ next if value_length.send(validity_check, check_value)
55
+ end
56
+
57
+ errors_options[:count] = check_value
58
+
59
+ default_message = options[MESSAGES[key]]
60
+ errors_options[:message] ||= default_message if default_message
61
+
62
+ record.errors.add(attribute, MESSAGES[key], errors_options)
63
+ end
64
+ end
65
+
66
+ private
67
+ def skip_nil_check?(key)
68
+ key == :maximum && options[:allow_nil].nil? && options[:allow_blank].nil?
69
+ end
70
+ end
71
+
72
+ module HelperMethods
73
+ # Validates that the specified attributes match the length restrictions
74
+ # supplied. Only one constraint option can be used at a time apart from
75
+ # +:minimum+ and +:maximum+ that can be combined together:
76
+ #
77
+ # class Person < ActiveRecord::Base
78
+ # validates_length_of :first_name, maximum: 30
79
+ # validates_length_of :last_name, maximum: 30, message: "less than 30 if you don't mind"
80
+ # validates_length_of :fax, in: 7..32, allow_nil: true
81
+ # validates_length_of :phone, in: 7..32, allow_blank: true
82
+ # validates_length_of :user_name, within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name'
83
+ # validates_length_of :zip_code, minimum: 5, too_short: 'please enter at least 5 characters'
84
+ # validates_length_of :smurf_leader, is: 4, message: "papa is spelled with 4 characters... don't play me."
85
+ # validates_length_of :words_in_essay, minimum: 100, too_short: 'Your essay must be at least 100 words.'
86
+ #
87
+ # private
88
+ #
89
+ # def words_in_essay
90
+ # essay.scan(/\w+/)
91
+ # end
92
+ # end
93
+ #
94
+ # Constraint options:
95
+ #
96
+ # * <tt>:minimum</tt> - The minimum size of the attribute.
97
+ # * <tt>:maximum</tt> - The maximum size of the attribute. Allows +nil+ by
98
+ # default if not used with +:minimum+.
99
+ # * <tt>:is</tt> - The exact size of the attribute.
100
+ # * <tt>:within</tt> - A range specifying the minimum and maximum size of
101
+ # the attribute.
102
+ # * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>.
103
+ #
104
+ # Other options:
105
+ #
106
+ # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
107
+ # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
108
+ # * <tt>:too_long</tt> - The error message if the attribute goes over the
109
+ # maximum (default is: "is too long (maximum is %{count} characters)").
110
+ # * <tt>:too_short</tt> - The error message if the attribute goes under the
111
+ # minimum (default is: "is too short (minimum is %{count} characters)").
112
+ # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt>
113
+ # method and the attribute is the wrong size (default is: "is the wrong
114
+ # length (should be %{count} characters)").
115
+ # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
116
+ # <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
117
+ # <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
118
+ #
119
+ # There is also a list of default options supported by every validator:
120
+ # +:if+, +:unless+, +:on+ and +:strict+.
121
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
122
+ def validates_length_of(*attr_names)
123
+ validates_with LengthValidator, _merge_attributes(attr_names)
124
+ end
125
+
126
+ alias_method :validates_size_of, :validates_length_of
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal/util"
4
+
5
+ module ActiveModel
6
+ module Validations
7
+ 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
11
+
12
+ RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
13
+
14
+ INTEGER_REGEX = /\A[+-]?\d+\z/
15
+
16
+ def check_validity!
17
+ keys = CHECKS.keys - [:odd, :even]
18
+ options.slice(*keys).each do |option, value|
19
+ unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
20
+ raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
21
+ end
22
+ end
23
+ end
24
+
25
+ def validate_each(record, attr_name, value)
26
+ came_from_user = :"#{attr_name}_came_from_user?"
27
+
28
+ if record.respond_to?(came_from_user)
29
+ if record.public_send(came_from_user)
30
+ raw_value = record.read_attribute_before_type_cast(attr_name)
31
+ elsif record.respond_to?(:read_attribute)
32
+ raw_value = record.read_attribute(attr_name)
33
+ end
34
+ else
35
+ before_type_cast = :"#{attr_name}_before_type_cast"
36
+ if record.respond_to?(before_type_cast)
37
+ raw_value = record.public_send(before_type_cast)
38
+ end
39
+ end
40
+ raw_value ||= value
41
+
42
+ if record_attribute_changed_in_place?(record, attr_name)
43
+ raw_value = value
44
+ end
45
+
46
+ unless is_number?(raw_value)
47
+ record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
48
+ return
49
+ end
50
+
51
+ if allow_only_integer?(record) && !is_integer?(raw_value)
52
+ record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
53
+ return
54
+ end
55
+
56
+ value = parse_as_number(raw_value)
57
+
58
+ options.slice(*CHECKS.keys).each do |option, option_value|
59
+ case option
60
+ when :odd, :even
61
+ unless value.to_i.send(CHECKS[option])
62
+ record.errors.add(attr_name, option, filtered_options(value))
63
+ end
64
+ else
65
+ case option_value
66
+ when Proc
67
+ option_value = option_value.call(record)
68
+ when Symbol
69
+ option_value = record.send(option_value)
70
+ end
71
+
72
+ option_value = parse_as_number(option_value)
73
+
74
+ unless value.send(CHECKS[option], option_value)
75
+ record.errors.add(attr_name, option, filtered_options(value).merge!(count: option_value))
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def is_number?(raw_value)
84
+ !parse_as_number(raw_value).nil?
85
+ rescue ArgumentError, TypeError
86
+ false
87
+ end
88
+
89
+ def parse_as_number(raw_value)
90
+ if raw_value.is_a?(Float)
91
+ raw_value.to_d
92
+ elsif raw_value.is_a?(Numeric)
93
+ raw_value
94
+ elsif is_integer?(raw_value)
95
+ raw_value.to_i
96
+ elsif !is_hexadecimal_literal?(raw_value)
97
+ Kernel.Float(raw_value).to_d
98
+ end
99
+ end
100
+
101
+ def is_integer?(raw_value)
102
+ INTEGER_REGEX === raw_value.to_s
103
+ end
104
+
105
+ def is_hexadecimal_literal?(raw_value)
106
+ /\A0[xX]/ === raw_value.to_s
107
+ end
108
+
109
+ def filtered_options(value)
110
+ filtered = options.except(*RESERVED_OPTIONS)
111
+ filtered[:value] = value
112
+ filtered
113
+ end
114
+
115
+ def allow_only_integer?(record)
116
+ case options[:only_integer]
117
+ when Symbol
118
+ record.send(options[:only_integer])
119
+ when Proc
120
+ options[:only_integer].call(record)
121
+ else
122
+ options[:only_integer]
123
+ end
124
+ end
125
+
126
+ def record_attribute_changed_in_place?(record, attr_name)
127
+ record.respond_to?(:attribute_changed_in_place?) &&
128
+ record.attribute_changed_in_place?(attr_name.to_s)
129
+ end
130
+ end
131
+
132
+ module HelperMethods
133
+ # Validates whether the value of the specified attribute is numeric by
134
+ # trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
135
+ # is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\z/</tt>
136
+ # (if <tt>only_integer</tt> is set to +true+).
137
+ #
138
+ # class Person < ActiveRecord::Base
139
+ # validates_numericality_of :value, on: :create
140
+ # end
141
+ #
142
+ # Configuration options:
143
+ # * <tt>:message</tt> - A custom error message (default is: "is not a number").
144
+ # * <tt>:only_integer</tt> - Specifies whether the value has to be an
145
+ # integer, e.g. an integral value (default is +false+).
146
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
147
+ # +false+). Notice that for Integer and Float columns empty strings are
148
+ # converted to +nil+.
149
+ # * <tt>:greater_than</tt> - Specifies the value must be greater than the
150
+ # supplied value.
151
+ # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be
152
+ # greater than or equal the supplied value.
153
+ # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied
154
+ # value.
155
+ # * <tt>:less_than</tt> - Specifies the value must be less than the
156
+ # supplied value.
157
+ # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less
158
+ # than or equal the supplied value.
159
+ # * <tt>:other_than</tt> - Specifies the value must be other than the
160
+ # supplied value.
161
+ # * <tt>:odd</tt> - Specifies the value must be an odd number.
162
+ # * <tt>:even</tt> - Specifies the value must be an even number.
163
+ #
164
+ # There is also a list of default options supported by every validator:
165
+ # +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
166
+ # See <tt>ActiveModel::Validations#validates</tt> for more information
167
+ #
168
+ # The following checks can also be supplied with a proc or a symbol which
169
+ # corresponds to a method:
170
+ #
171
+ # * <tt>:greater_than</tt>
172
+ # * <tt>:greater_than_or_equal_to</tt>
173
+ # * <tt>:equal_to</tt>
174
+ # * <tt>:less_than</tt>
175
+ # * <tt>:less_than_or_equal_to</tt>
176
+ # * <tt>:only_integer</tt>
177
+ #
178
+ # For example:
179
+ #
180
+ # class Person < ActiveRecord::Base
181
+ # validates_numericality_of :width, less_than: ->(person) { person.height }
182
+ # validates_numericality_of :width, greater_than: :minimum_weight
183
+ # end
184
+ def validates_numericality_of(*attr_names)
185
+ validates_with NumericalityValidator, _merge_attributes(attr_names)
186
+ end
187
+ end
188
+ end
189
+ end