activemodel 5.2.3

Sign up to get free protection for your applications and to get access to all the features.
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