Check for out of range float values

Remove unsupported attribute `[maxlength]` from `input[number]`.
This commit is contained in:
2026-07-20 16:39:35 +02:00
parent ef8214cfa7
commit 5d051de666
10 changed files with 88 additions and 38 deletions

View File

@@ -101,22 +101,14 @@ module ApplicationHelper
end
def number_field(method, options = {})
attr_type = object.type_for_attribute(method)
case attr_type.type
case object.type_for_attribute(method).type
when :float, :double
options[:value] = object.public_send(method)&.to_scientific
options[:step] ||= :any
options[:min] ||= Float::MIN_15
options[:max] ||= Float::MAX_15
# Longest possible number (written not using exponent):
# sign (1), leading 0 (1), dot (1), exponent 0s (307), digits (15).
# This is only upper bound, which cannot guarantee the number won't fall
# out of range.
# TODO: add `[pattern]` to limit precision and (possibly) replace `[maxlength]`?
# NOTE: `[pattern]` is unavailable on `input[type=number]` and `[min]/[max]` is
# unavailable on `input[type=text]`.
options[:maxlength] ||= 3 + Float::MIN_10_EXP + Float::DIG
options[:min] ||= Float::MIN_15.to_scientific
options[:max] ||= Float::MAX_15.to_scientific
options[:oninput] = 'numberValidate(event)'
options[:size] ||= 6
options[:step] ||= :any
options[:value] = object.public_send("#{method}_before_type_cast")
end
super
end

View File

@@ -53,6 +53,27 @@ function formValidate(event) {
}
window.formValidate = formValidate
// Pattern for number precision verification. Should accept all number formats
// accepted by Kernel#Float. Lookahead checks the number of significant digits
// in mantissa, following expression makes sure there is only one dot.
const numberRegExp = /^\-?(?=(?:\.?0)*(?:\.?\d){1,15}?(?:\.?0)*\.?(?:[eE]|$))(?<mantissa>\d*\.?\d+|\d+\.)(?:[eE]\-?\d{1,3})?$/
// Validation of input[type=number] value precision.
function numberValidate(event) {
var input = event.target
input.setCustomValidity("")
if (!input.validity.valid) return
var match = numberRegExp.exec(input.value)
if (!match ||
(parseFloat(input.value) == 0.0 && match.groups['mantissa'].match(/[1-9]/))) {
// TODO: move message to locales
input.setCustomValidity("Please select a value within range of double precision floating-point format.")
}
}
window.numberValidate = numberValidate
// Turbo stream actions.
const FORM_CONTROLS = ["BUTTON", "FIELDSET", "INPUT", "OPTGROUP", "OPTION", "SELECT",