From 15a5515c996a3f6134ef7425944df1b98c221e87 Mon Sep 17 00:00:00 2001 From: cryptogopher Date: Sat, 7 Dec 2024 20:16:43 +0100 Subject: [PATCH] Extend NumericalityValidator to check precision and scale Use new checks on Unit.multiplier Closes #28 --- app/models/unit.rb | 2 +- app/views/units/_form.html.erb | 3 ++- config/initializers/core_ext.rb | 4 ++++ config/locales/en.yml | 4 ++++ ...numericality_validates_precision_and_scale.rb | 16 ++++++++++++++++ 5 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 lib/core_ext/active_model/validations/numericality_validates_precision_and_scale.rb diff --git a/app/models/unit.rb b/app/models/unit.rb index 027c492..c40753c 100644 --- a/app/models/unit.rb +++ b/app/models/unit.rb @@ -13,7 +13,7 @@ class Unit < ApplicationRecord length: {maximum: columns_hash['symbol'].limit} validates :description, length: {maximum: columns_hash['description'].limit} validates :multiplier, numericality: {equal_to: 1}, unless: :base - validates :multiplier, numericality: {other_than: 0}, if: :base + validates :multiplier, numericality: {other_than: 0, precision: true, scale: true}, if: :base scope :defaults, ->{ where(user: nil) } scope :defaults_diff, ->{ diff --git a/app/views/units/_form.html.erb b/app/views/units/_form.html.erb index 3d4dfc6..cc77f6a 100644 --- a/app/views/units/_form.html.erb +++ b/app/views/units/_form.html.erb @@ -13,7 +13,8 @@ <% unless @unit.base.nil? %> <%= form.hidden_field :base_id, form: :unit_form %> - <%= form.number_field :multiplier, form: :unit_form, required: true, step: "any", + <%= form.number_field :multiplier, form: :unit_form, required: true, + step: BigDecimal(10).power(-@unit.class.type_for_attribute(:multiplier).scale), size: 10, autocomplete: "off" %> <% end %> diff --git a/config/initializers/core_ext.rb b/config/initializers/core_ext.rb index 54a1409..fe6bd62 100644 --- a/config/initializers/core_ext.rb +++ b/config/initializers/core_ext.rb @@ -1,5 +1,9 @@ require 'core_ext/big_decimal_scientific_notation' +ActiveSupport.on_load :active_record do + ActiveModel::Validations::NumericalityValidator.prepend CoreExt::ActiveModel::Validations::NumericalityValidatesPrecisionAndScale +end + ActiveSupport.on_load :action_dispatch_system_test_case do prepend CoreExt::ActionDispatch::SystemTesting::TestHelpers::ScreenshotHelperUniqueId end diff --git a/config/locales/en.yml b/config/locales/en.yml index 2bce615..1fa61c1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,4 +1,8 @@ en: + errors: + messages: + precision_exceeded: must not exceed %{value} significant digits + scale_exceeded: must not exceed %{value} decimal digits activerecord: attributes: unit: diff --git a/lib/core_ext/active_model/validations/numericality_validates_precision_and_scale.rb b/lib/core_ext/active_model/validations/numericality_validates_precision_and_scale.rb new file mode 100644 index 0000000..a8fe744 --- /dev/null +++ b/lib/core_ext/active_model/validations/numericality_validates_precision_and_scale.rb @@ -0,0 +1,16 @@ +module CoreExt::ActiveModel::Validations::NumericalityValidatesPrecisionAndScale + def validate_each(record, attr_name, value, ...) + super(record, attr_name, value, ...) + + if options[:precision] || options[:scale] + attr_type = record.class.type_for_attribute(attr_name) + value = BigDecimal(value) unless value.is_a? BigDecimal + if options[:precision] && (value.precision > attr_type.precision) + record.errors.add(attr_name, :precision_exceeded, **filtered_options(attr_type.precision)) + end + if options[:scale] && (value.scale > attr_type.scale) + record.errors.add(attr_name, :scale_exceeded, **filtered_options(attr_type.scale)) + end + end + end +end