diff --git a/.mod/drivers-evergreen-tools b/.mod/drivers-evergreen-tools index 1f018c7a24..14cc285a38 160000 --- a/.mod/drivers-evergreen-tools +++ b/.mod/drivers-evergreen-tools @@ -1 +1 @@ -Subproject commit 1f018c7a248c4fcda6cb7a77043fd673755e0986 +Subproject commit 14cc285a383f79eb2d0aec95e611192da9aa9dad diff --git a/lib/mongoid/validatable.rb b/lib/mongoid/validatable.rb index ce7f5c4c1d..921f417984 100644 --- a/lib/mongoid/validatable.rb +++ b/lib/mongoid/validatable.rb @@ -6,6 +6,7 @@ require "mongoid/validatable/associated" require "mongoid/validatable/format" require "mongoid/validatable/length" +require "mongoid/validatable/numericality" require "mongoid/validatable/queryable" require "mongoid/validatable/presence" require "mongoid/validatable/uniqueness" diff --git a/lib/mongoid/validatable/macros.rb b/lib/mongoid/validatable/macros.rb index 363af222d6..bb0a081ebe 100644 --- a/lib/mongoid/validatable/macros.rb +++ b/lib/mongoid/validatable/macros.rb @@ -89,6 +89,21 @@ def validates_length_of(*args) def validates_presence_of(*args) validates_with(PresenceValidator, _merge_attributes(args)) end + + # Validates whether or not a field contains a numeric value. + # + # @example + # class Person + # include Mongoid::Document + # field :cost + # + # validates_numericality_of :cost + # end + # + # @param [ Object... ] *args The names of the field(s) to validate. + def validates_numericality_of(*args) + validates_with(NumericalityValidator, _merge_attributes(args)) + end end end end diff --git a/lib/mongoid/validatable/numericality.rb b/lib/mongoid/validatable/numericality.rb new file mode 100644 index 0000000000..fa1bf276b3 --- /dev/null +++ b/lib/mongoid/validatable/numericality.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mongoid + module Validatable + # A specialization of the ActiveModel numericality validator, which adds + # logic to recognize and accept BSON::Decimal128 as a number. + class NumericalityValidator < ActiveModel::Validations::NumericalityValidator + private + + # Ensure that BSON::Decimal128 is treated as a BigDecimal during the + # validation step. + def prepare_value_for_validation(value, record, attr_name) + result = super + + result.is_a?(BSON::Decimal128) ? result.to_big_decimal : result + end + end + end +end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 59cdb6d7b3..4112115fa5 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -127,6 +127,12 @@ def prepare_new_rails_app(name) end context 'new application - rails' do + before(:all) do + if SpecConfig.instance.rails_version < '7.1' + skip '`rails new` with rails < 7.1 fails because modern concurrent-ruby removed logger dependency' + end + end + it 'creates' do prepare_new_rails_app 'mongoid-test' do check_call(%w(rails g model post), env: clean_env) diff --git a/spec/mongoid/validatable/numericality_spec.rb b/spec/mongoid/validatable/numericality_spec.rb index ea89608d08..87b8c91740 100644 --- a/spec/mongoid/validatable/numericality_spec.rb +++ b/spec/mongoid/validatable/numericality_spec.rb @@ -29,5 +29,21 @@ class TestModel expect(model).to_not be_valid end end + + context 'when the value is numeric' do + let(:model) { TestModel.new(amount: '15.0') } + + it 'returns true' do + expect(model).to be_valid + end + end + + context 'when the value is a BSON::Decimal128' do + let(:model) { TestModel.new(amount: BSON::Decimal128.new('15.0')) } + + it 'returns true' do + expect(model).to be_valid + end + end end end