diff --git a/lib/simple_form/helpers/validators.rb b/lib/simple_form/helpers/validators.rb index 6ca6798b..a0312fca 100644 --- a/lib/simple_form/helpers/validators.rb +++ b/lib/simple_form/helpers/validators.rb @@ -21,7 +21,46 @@ def valid_validator?(validator) end def conditional_validators?(validator) - validator.options.include?(:if) || validator.options.include?(:unless) + if_condition = validator.options[:if] + unless_condition = validator.options[:unless] + + return false if if_condition.nil? && unless_condition.nil? + + conditional = false + + if if_condition + conditions = Array(if_condition) + conditional = conditions.any? { |c| !resolve_if_condition(c) } + end + + if !conditional && unless_condition + conditions = Array(unless_condition) + conditional = conditions.any? { |c| resolve_if_condition(c) } + end + + conditional + # If evaluating the condition fails (e.g., the condition references a method + # the object doesn't have, or a callable with unexpected arity), fall back to + # treating the validator as conditional and skipping it. + rescue NoMethodError, ArgumentError + true + end + + def resolve_if_condition(condition) + case condition + when Symbol + object.send(condition) + else + if condition.respond_to?(:call) + if condition.is_a?(Proc) && condition.arity == 0 + condition.call + else + condition.call(object) + end + else + condition + end + end end def action_validator_match?(validator) diff --git a/test/inputs/required_test.rb b/test/inputs/required_test.rb index 26ef7067..b5053502 100644 --- a/test/inputs/required_test.rb +++ b/test/inputs/required_test.rb @@ -82,20 +82,32 @@ class RequiredTest < ActionView::TestCase end # VALIDATORS :if :unless - test 'builder input does not be required when ActiveModel::Validations is included and if option is present' do + test 'builder input is required when if condition is present and evaluates to true' do + with_form_for @validating_user, :age + assert_select 'input.required[required]#validating_user_age' + end + + test 'builder input is not required when if condition is present and evaluates to false' do + @validating_user.name = nil with_form_for @validating_user, :age assert_no_select 'input.required' assert_no_select 'input[required]' assert_select 'input.optional#validating_user_age' end - test 'builder input does not be required when ActiveModel::Validations is included and unless option is present' do + test 'builder input is not required when unless condition is present and evaluates to true' do with_form_for @validating_user, :amount assert_no_select 'input.required' assert_no_select 'input[required]' assert_select 'input.optional#validating_user_amount' end + test 'builder input is required when unless condition is present and evaluates to false' do + @validating_user.age = nil + with_form_for @validating_user, :amount + assert_select 'input.required[required]#validating_user_amount' + end + # VALIDATORS :on test 'builder input is required when validation is on create and is not persisted' do @validating_user.new_record!