Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check conditions for presence validators to see if an attribute is required #693

Merged
merged 1 commit into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ You may find the [demo application](#the-demo-application) useful for developmen

- If you've never made a pull request (PR) before, read [this](https://help.github.com/articles/about-pull-requests/).
- If your PR fixes an issues, be sure to put "Fixes #nnn" in the description of the PR (where `nnn` is the issue number). Github will automatically close the issue when the PR is merged.
- When the PR is submitted, check if Travis CI ran all the tests successfully, and didn't raise any issues.
- When the PR is submitted, check if GitHub Actions ran all the tests successfully, and didn't raise any issues.

### 7. Done

Expand Down
29 changes: 17 additions & 12 deletions lib/bootstrap_form/components/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,34 @@ def required_attribute?(obj, attribute)
target = obj.instance_of?(Class) ? obj : obj.class
return false unless target.respond_to? :validators_on

presence_validator?(target_unconditional_validators(target, attribute)) ||
required_association?(target, attribute)
presence_validators?(target, obj, attribute) || required_association?(target, obj, attribute)
end

def required_association?(target, attribute)
target.try(:reflections)&.find do |name, a|
def required_association?(target, object, attribute)
target.try(:reflections)&.any? do |name, a|
next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection)
next unless a.foreign_key == attribute.to_s

presence_validator?(target_unconditional_validators(target, name))
presence_validators?(target, object, name)
end
end

def target_unconditional_validators(target, attribute)
target.validators_on(attribute)
.reject { |validator| validator.options[:if].present? || validator.options[:unless].present? }
.map(&:class)
def presence_validators?(target, object, attribute)
target.validators_on(attribute).select { |v| presence_validator?(v.class) }.any? do |validator|
if_option = validator.options[:if]
unless_opt = validator.options[:unless]
(!if_option || call_with_self(object, if_option)) && (!unless_opt || !call_with_self(object, unless_opt))
Comment on lines +43 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we now handle conditional validators, right?

end
end

def call_with_self(object, proc)
object.instance_exec(*[(object if proc.arity >= 1)].compact, &proc)
end

def presence_validator?(target_validators)
target_validators.include?(ActiveModel::Validations::PresenceValidator) ||
def presence_validator?(validator_class)
validator_class == ActiveModel::Validations::PresenceValidator ||
(defined?(ActiveRecord::Validations::PresenceValidator) &&
target_validators.include?(ActiveRecord::Validations::PresenceValidator))
validator_class == ActiveRecord::Validations::PresenceValidator)
Comment on lines +54 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never understood why the second part of the || is going to make a difference.

end

def inline_error?(name)
Expand Down
7 changes: 5 additions & 2 deletions lib/bootstrap_form/helpers/field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ module Field
def required_field_options(options, method)
required = required_field?(options, method)
{}.tap do |option|
option[:required] = required
option[:aria] = { required: true } if required
if required
option[:required] = true
option[:aria] ||= {}
option[:aria][:required] = true
end
Comment on lines +7 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

end
end

Expand Down