-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Support fail_fast param validation option #1499
Support fail_fast param validation option #1499
Conversation
18c27b5
to
32de18e
Compare
@@ -140,9 +140,13 @@ def require_optional_fields(context, opts) | |||
end | |||
|
|||
def validate_attributes(attrs, opts, &block) | |||
# check fail_fast flag | |||
fail_fast = opts.delete(:fail_fast) || false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not be modifying opts
being passed in here, combine the clone
below.
@@ -1355,6 +1355,23 @@ subject.rescue_from Grape::Exceptions::ValidationErrors do |e| | |||
end | |||
``` | |||
|
|||
Grape returns all validation and coercion errors found by default. To skip all subsequent validation checks when a specific param is found invalid, use `fail_fast: true`. | |||
For example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe space things up a bit. Minor.
required :wine | ||
end | ||
``` | ||
will not check if `:wine` is present unless it finds `:beer`. The result of empty params would be a single `Grape::Exceptions::ValidationErrors` error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a lot of grape examples try to make full sentences, so prefer "The following example will not ..." to having "For example: [code] will not".
@@ -201,7 +205,7 @@ def configure_declared_params | |||
end | |||
end | |||
|
|||
def validates(attrs, validations) | |||
def validates(attrs, validations, fail_fast = false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the argument should be an options hash here, so we can make it extensible for the future. So def validates(attrs, validations, opts = {})
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -10,10 +10,11 @@ class Base | |||
# @param options [Object] implementation-dependent Validator options | |||
# @param required [Boolean] attribute(s) are required or optional | |||
# @param scope [ParamsScope] parent scope for this Validator | |||
def initialize(attrs, options, required, scope) | |||
def initialize(attrs, options, required, fail_fast, scope) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should fail_fast
be rolled into one of these existing params? I am worried about custom validators other people built.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dblock I've thought about it. It could be a part of the scope, but I am not sure if it is the best place for validation options. Thoughts?
I've changed the constructor to the following to keep existing custom validators working.
def initialize(attrs, options, required, scope, opts = {})
This is excellent. Most of my comments are small implementation-related things, I'd like |
@@ -140,9 +140,14 @@ def require_optional_fields(context, opts) | |||
end | |||
|
|||
def validate_attributes(attrs, opts, &block) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried to do this?
def validate_attributes(attrs, validations, opts = {}, &block)
This way there wouldn't be any reason to do the selection below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way this seems to be a bit too much future-proof ;)
validations = opts.clone
validations[:type] ||= Array if block
options = {}
options[:fail_fast] = validations.delete(:fail_fast)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... which begs the question, why is fail_fast
so special? Why does it need to be split at all? Why can't it just be @fail_fast = validations[:fail_fast]
and not pass anything more around?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I would have to split the options here, no?
Hmm, I guess I got a bit carried away with the future other options idea :)
In my opinion, the difference is that the rest of the validations
refer to a specific validation type with options whereas fail_fast
(and possible future validation options) influences the validation process across multiple validations.
But I could be wrong. I just dove into grape code for the first time :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to make it simpler for now, if not splitting is less code, we should go with that.
This is better. Merging. |
#1423
This change will enable
which will result in skipping of subsequent validation checks if a fail_fast required or optional fail validation.