Skip to content

Commit 722c78e

Browse files
Handle length validator with procs
Devise changed their password validations to use a proc for minimum/maximum for additional customization, but that apparently broke SimpleForm minlength/maxlength logic since it wasn't handling that. Instead of implementing it custom by checking for the value responding to `call` like we do for a couple of other validators, I went with the route of copying over the `resolve_value` implementation added to Rails 7.1 that handles all the possible scenarios for procs / send / etc, which allows different ways of setting up the proc. Closes #1858
1 parent 584127a commit 722c78e

File tree

6 files changed

+41
-5
lines changed

6 files changed

+41
-5
lines changed

lib/simple_form/components/maxlength.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def find_length_validator
2626

2727
def maximum_length_value_from(length_validator)
2828
if length_validator
29-
length_validator.options[:is] || length_validator.options[:maximum]
29+
value = length_validator.options[:is] || length_validator.options[:maximum]
30+
resolve_validator_value(value)
3031
end
3132
end
3233
end

lib/simple_form/components/minlength.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def find_length_validator
2626

2727
def minimum_length_value_from(length_validator)
2828
if length_validator
29-
length_validator.options[:is] || length_validator.options[:minimum]
29+
value = length_validator.options[:is] || length_validator.options[:minimum]
30+
resolve_validator_value(value)
3031
end
3132
end
3233
end

lib/simple_form/helpers/validators.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,27 @@ def action_validator_match?(validator)
4040
def find_validator(kind)
4141
attribute_validators.find { |v| v.kind == kind } if has_validators?
4242
end
43+
44+
# Implements `ActiveModel::Validations::ResolveValue`, introduced by Rails 7.1.
45+
# https://github.com/rails/rails/blob/v7.1.0/activemodel/lib/active_model/validations/resolve_value.rb
46+
def resolve_validator_value(value)
47+
case value
48+
when Proc
49+
if value.arity == 0
50+
value.call
51+
else
52+
value.call(object)
53+
end
54+
when Symbol
55+
object.send(value)
56+
else
57+
if value.respond_to?(:call)
58+
value.call(object)
59+
else
60+
value
61+
end
62+
end
63+
end
4364
end
4465
end
4566
end

test/inputs/string_input_test.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,26 @@ class StringInputTest < ActionView::TestCase
3333
assert_select 'input.password[type=password][maxlength="100"]'
3434
end
3535

36-
test 'input infers maxlength column definition from validation when present' do
36+
test 'input infers maxlength from validation when present' do
3737
with_input_for @validating_user, :name, :string
3838
assert_select 'input.string[maxlength="25"]'
3939
end
4040

41-
test 'input infers minlength column definition from validation when present' do
41+
test 'input infers minlength from validation when present' do
4242
with_input_for @validating_user, :name, :string
4343
assert_select 'input.string[minlength="5"]'
4444
end
4545

46+
test 'input infers maxlength from validation proc when present' do
47+
with_input_for @validating_user, :username, :string
48+
assert_select 'input.string[maxlength="30"]'
49+
end
50+
51+
test 'input infers minlength from validation proc when present' do
52+
with_input_for @validating_user, :username, :string
53+
assert_select 'input.string[minlength="10"]'
54+
end
55+
4656
test 'input gets maxlength from validation when :is option present' do
4757
with_input_for @validating_user, :home_picture, :string
4858
assert_select 'input.string[maxlength="12"]'

test/support/models.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class User
8888
extend ActiveModel::Naming
8989
include ActiveModel::Conversion
9090

91-
attr_accessor :id, :name, :company, :company_id, :time_zone, :active, :age,
91+
attr_accessor :id, :name, :username, :company, :company_id, :time_zone, :active, :age,
9292
:description, :created_at, :updated_at, :credit_limit, :password, :url,
9393
:delivery_time, :born_at, :special_company_id, :country, :tags, :tag_ids,
9494
:avatar, :home_picture, :email, :status, :residence_country, :phone_number,
@@ -261,6 +261,7 @@ def self.readonly_attributes
261261
class ValidatingUser < User
262262
include ActiveModel::Validations
263263
validates :name, presence: true
264+
validates :username, presence: true
264265
validates :company, presence: true
265266
validates :age, presence: true, if: proc { |user| user.name }
266267
validates :amount, presence: true, unless: proc { |user| user.age }
@@ -282,6 +283,7 @@ class ValidatingUser < User
282283
less_than_or_equal_to: :max_attempts,
283284
only_integer: true
284285
validates_length_of :name, maximum: 25, minimum: 5
286+
validates_length_of :username, maximum: -> { 30 }, minimum: -> { 10 }
285287
validates_length_of :description, in: 15..50
286288
validates_length_of :home_picture, is: 12
287289

test/test_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def setup_users(extra_attributes = {})
5656

5757
@validating_user = ValidatingUser.build({
5858
name: 'Tester McTesterson',
59+
username: 'mctesterson',
5960
description: 'A test user of the most distinguished caliber',
6061
home_picture: 'Home picture',
6162
age: 19,

0 commit comments

Comments
 (0)