From 1f38753e619366464a6ab45da2046740132c4210 Mon Sep 17 00:00:00 2001 From: Geremia Taglialatela Date: Sun, 4 Jan 2026 23:49:26 +0100 Subject: [PATCH] Allow `proc` values in lenght validator options Fix #997 --- .rubocop.yml | 5 +++- CHANGELOG.md | 1 + lib/client_side_validations/active_model.rb | 10 +++++++- .../active_model/format.rb | 4 ++-- .../active_model/length.rb | 8 ++++++- .../active_model/numericality.rb | 2 +- .../cases/test_length_validator.rb | 24 +++++++++++++++++++ 7 files changed, 48 insertions(+), 6 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 04770ae7a..9c7494126 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -27,7 +27,7 @@ Layout/LineLength: Enabled: false Metrics/AbcSize: - Max: 21.91 + Max: 24.52 Metrics/BlockLength: Exclude: @@ -87,3 +87,6 @@ Style/FormatStringToken: Style/IfUnlessModifier: Enabled: false + +Style/NumericPredicate: + Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index b0aad575f..7784b9641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * [FEATURE] Drop Internet Explorer and other older browsers support * [FEATURE] Drop Ruby < 3.1 * [FEATURE] Drop jQuery < 3.7.1 Compatibility +* [BUGFIX] Allow `proc` values in length validator options * [ENHANCEMENT] Update QUnit to 2.24.3 * [ENHANCEMENT] Test against Ruby 3.4 diff --git a/lib/client_side_validations/active_model.rb b/lib/client_side_validations/active_model.rb index d9a1bf074..f6ac2675f 100644 --- a/lib/client_side_validations/active_model.rb +++ b/lib/client_side_validations/active_model.rb @@ -45,6 +45,14 @@ def message_type def callbacks_options ::ActiveModel::Error::CALLBACKS_OPTIONS end + + def resolve_proc(value, object) + if value.arity == 0 + value.call + else + value.call(object) + end + end end module Validations @@ -172,7 +180,7 @@ def client_side_hash(model, attribute, force = nil) if options[:in].respond_to?(:call) return unless force - options[:in] = options[:in].call(model) + options[:in] = resolve_proc(options[:in], model) end hash = build_client_side_hash(model, attribute, options) diff --git a/lib/client_side_validations/active_model/format.rb b/lib/client_side_validations/active_model/format.rb index c6c7ebbdc..3d7558e19 100644 --- a/lib/client_side_validations/active_model/format.rb +++ b/lib/client_side_validations/active_model/format.rb @@ -8,12 +8,12 @@ def client_side_hash(model, attribute, force = nil) if options[:with].respond_to?(:call) return unless force - options[:with] = options[:with].call(model) + options[:with] = resolve_proc(options[:with], model) build_client_side_hash(model, attribute, options) elsif options[:without].respond_to?(:call) return unless force - options[:without] = options[:without].call(model) + options[:without] = resolve_proc(options[:without], model) build_client_side_hash(model, attribute, options) else super diff --git a/lib/client_side_validations/active_model/length.rb b/lib/client_side_validations/active_model/length.rb index c7275b047..8643e3a34 100644 --- a/lib/client_side_validations/active_model/length.rb +++ b/lib/client_side_validations/active_model/length.rb @@ -3,7 +3,7 @@ module ClientSideValidations module ActiveModel module Length - def client_side_hash(model, attribute, _force = nil) + def client_side_hash(model, attribute, force = nil) options = self.options.dup hash = options_hash(options) @@ -11,6 +11,12 @@ def client_side_hash(model, attribute, _force = nil) count = options[option] next unless count + if count.respond_to?(:call) + next unless force + + count = resolve_proc(count, model) + end + options[:message] = options[message_type] if options[message_type].present? options.delete(:message) if options[:message].nil? hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(count: count)) diff --git a/lib/client_side_validations/active_model/numericality.rb b/lib/client_side_validations/active_model/numericality.rb index 5690dbe2d..14b6e1970 100644 --- a/lib/client_side_validations/active_model/numericality.rb +++ b/lib/client_side_validations/active_model/numericality.rb @@ -23,7 +23,7 @@ def client_side_hash(model, attribute, force = nil) if count.respond_to?(:call) next unless force - count = count.call(model) + count = resolve_proc(count, model) end hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(count: count)) diff --git a/test/active_model/cases/test_length_validator.rb b/test/active_model/cases/test_length_validator.rb index 65a78bd9f..9f966b307 100644 --- a/test/active_model/cases/test_length_validator.rb +++ b/test/active_model/cases/test_length_validator.rb @@ -70,5 +70,29 @@ def test_length_client_side_hash_with_range assert_equal expected_hash, LengthValidator.new(attributes: [:age], within: 5..10).client_side_hash(@person, :age) end + + def test_length_client_side_hash_with_minimum_and_maximum_proc_force + expected_hash = { + messages: { + minimum: 'is too short (minimum is 5 characters)', + maximum: 'is too long (maximum is 10 characters)' + }, + minimum: 5, + maximum: 10 + } + + assert_equal expected_hash, LengthValidator.new(attributes: [:age], minimum: proc { 5 }, maximum: proc { 10 }).client_side_hash(@person, :age, force: true) + end + + def test_length_client_side_hash_with_is_proc_force + expected_hash = { + messages: { + is: 'is the wrong length (should be 10 characters)' + }, + is: 10 + } + + assert_equal expected_hash, LengthValidator.new(attributes: [:age], is: proc { 10 }).client_side_hash(@person, :age, force: true) + end end end