-
-
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
The length
validator only takes effect for parameters with types that support #length
method
#2464
The length
validator only takes effect for parameters with types that support #length
method
#2464
Conversation
Ah nice catch 👏 for some reason I thought the validator won't be called if the parameter was not present (and optional) 😅 |
@OuYangJinTing care to answer the q above and update CHANGELOG, please? |
33d25b7
to
c7d4d8e
Compare
If the optional parameters are only passed, the verification will also be performed. |
@dblock Done ~ |
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 have some nits, but more importantly, do we have a test for "If you do not allow nil, you can use the allow_blank: false
option." If not please add?
c7d4d8e
to
171407a
Compare
@dblock Thank you for your feedback. At present, all feedback has been resolved. Please take a look when you have 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.
Looks good, one more typo below.
end | ||
end | ||
|
||
context 'When a nil value is passed' do |
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.
Nit: lowercase "When" please?
README.md
Outdated
@@ -1713,6 +1713,7 @@ end | |||
#### `length` | |||
|
|||
Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option. | |||
In addition, if the received parameter value is `nil`, the length validation will not be triggered. If you do not allow `nil`, you can use the `allow_blank: false` option. |
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.
Add "want to", so "If you do not want to allow..."
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.
um this will be problematic if you want to allow zero length parameters. For example
requires :some_list, type: [Integer], length: { min: 0, max: 10 }, allow_blank: false
requires :some_string, type: String, length: { min: 0 }, allow_blank: false
would both have validation errors if we provided the following parameters { some_list: [], some_string: '' }
IMO this is incorrect behaviour.
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 this is exactly the expected behavior. Why do you think this is incorrect @dhruvCW?
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 see like I mentioned combining the two can lead to unexpected results since allow_blank validates both nil as well as empty values. I personally then would appreciate this specific case to be documented so that users are aware about this potential pitfall.
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.
Yes, we should, @OuYangJinTing add specs for ^ and README pls?
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 @dhruvCW Sorry, I'm a little busy with work these two days, and I didn't give timely feedback.
Based on this consideration, I think it should be better to add an allow_nil option to the Grape::Validations::Validators::LengthValidator
. e.g:
module Grape
module Validations
module Validators
class LengthValidator < Base
def initialize(attrs, options, required, scope, **opts)
@min = options[:min]
@max = options[:max]
@allow_nil = options.key?(:allow_nil) ? options[:allow_nil] : true # allow `allow_nil` option, default to `true`
super
raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
end
def validate_param!(attr_name, params)
param = params[attr_name]
return if param.nil? && @allow_nil # when `allow_nil` is `true`, `nil` is allowed
raise ArgumentError, "parameter #{param} does not support #length" unless param.respond_to?(:length)
return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max)
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
end
...
end
If you agree, I will be happy to change it.
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 agree but I think the default should be false
. maybe ignore_nil
would be a better name ? since we are skipping the validation for nil
values.
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.
@dhruvCW Thank you for your suggestions.
- I agree that the default value be to false.
ignore_nil
is a good name, but I use the nameallow_nil
, considering that there is alreadyallow_blank
, and it should be better to use a similar name.
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.
ignore_nil is a good name, but I use the name allow_nil, considering that there is already allow_blank, and it should be better to use a similar name.
makes sense to keep the names consistent 😅 👍
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.
Yes, the other reason not to change these is to remain positive vs. negative.
@@ -18,6 +18,8 @@ def initialize(attrs, options, required, scope, **opts) | |||
def validate_param!(attr_name, params) | |||
param = params[attr_name] | |||
|
|||
return if param.nil? |
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.
Um should we ignore the validator if the parameter is required and the value is nil ?
IMO this is unexpected behavior right ? If a parameter is expected (thus marked as required) a nil value is technically incorrect. If we want to allow nil values we should mark the parameter as optional.
WDYT @dblock ?
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.
A nil
value is a perfectly valid value, that's why we have allow_blank
(to distinguish the cases when the parameter itself is required and a value for the parameter is required).
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 see I guess I contravened required with nil values. 😅 Sorry about that.
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 know it's confusing. We have gone back and forth on this 🙈
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’ll wait to merge this till everyone above is happy, including myself :) Please say “I’m good with merging this” when you are! And thank you so much for having an interesting conversation!
5f6c279
to
99f66eb
Compare
nil
in length
validator
Ok, it's just an option on |
requires :list, type: [Integer], length: { min: 0, allow_nil: true } | ||
end | ||
post 'allow_nil_param' do | ||
end |
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.
Add a test with an explicit allow_nil: 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.
We still need one test with the default (without any specification of allow_nil
).
I'd love some other people's opinion on whether we do want the |
99f66eb
to
63534f3
Compare
I think it's not suitable to add the |
@@ -18,6 +19,12 @@ def initialize(attrs, options, required, scope, **opts) | |||
def validate_param!(attr_name, params) | |||
param = params[attr_name] | |||
|
|||
if param.nil? | |||
return if @allow_nil |
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 was looking into the code to understand how allow_blank
interops with other validators and saw that it's an attribute in the base class.
So should we just use this option instead of introducing allow_nil
?
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.
That was my thinking. 🤷♂️
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 can help decide: does allow_nil
actually behave like allow_blank
in this implementation? Meaning, from the API point-of-view is nil
vs. []
treated the same?
In Ruby it's obviously not.
2.7.7 :001 > [].blank?
=> true
2.7.7 :002 > nil.blank?
=> true
vs.
2.7.7 :003 > [].nil?
=> false
2.7.7 :004 > nil.nil?
=> true
If they are, then allow_blank
makes sense. But if they aren't, then we should keep allow_nil
because it explicitly checks for nil
. Then, we should have an implementation for allow_blank?
as well to distinguish the two.
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.
technically no since allow_nil
is for only nil
values while allow_blank
would mean ignoring both nil
and empty
values. Not sure if we want to use allow_blank
or allow_nil
.
I personally do prefer more specific constraints thus allow_nil
but I am also okay if we decide to ignore blank
values if the allow_blank
flag is set to true.
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.
@OuYangJinTing write specs for these scenarios and let's decide?
Again, thanks so much for being patient. API changes are one-way, we can't easily undo them, so it's worth spending the 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.
@dhruvCW Introducing allow_nil
here only will make it potentially different from other parameter types. Without looking at specs and current behavior I would say allow_blank
for arrays/lists should behave like blank?
and if we wanted a allow_nil
then we should probably implement it for all other parameter types next to allow_blank
.
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.
@OuYangJinTing write specs for these scenarios and let's decide?
Again, thanks so much for being patient. API changes are one-way, we can't easily undo them, so it's worth spending the time.
@dblock Sorry, I'm a little confused.
Do you need to rollback to commit 171407aeef935ab918335c5ea324ab18a96a19d4 and provide a more detailed explanation?
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.
Let's wait for @ericproulx to help us out with a strong opinion
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.
Hmm, I'll make the changes after everyone finished feedback.
Sorry for my late response. Quite busy. I'll take a closer look this week-end |
There's is still something confusing about the params do
requires :list, type: [Integer], length: { min: 1, allow_nil: true }
end Does it mean that Although, I've noticed something regarding the describe 'when allow_blank is declared before length validator' do
subject { last_response }
let(:app) do
Class.new(Grape::API) do
params do
requires :list, type: [Integer], allow_blank: false, length: { max: 1 }, fail_fast: true
end
get do
end
end
end
before do
get '/', list: nil
end
it { is_expected.to be_bad_request}
end but this one will ❌ and raise the describe 'when allow_blank is declared after length validator' do
subject { last_response }
let(:app) do
Class.new(Grape::API) do
params do
requires :list, type: [Integer], length: { max: 1 }, fail_fast: true, allow_blank: false
end
get do
end
end
end
before do
get '/', list: nil
end
it { is_expected.to be_bad_request}
end Based on that behavior, the Also, regarding the |
ee5298f
to
ec83cc0
Compare
nil
in length
validatornil
in length
validator
@ericproulx Thank you for your detailed insights and explanations with examples.👍 Considering the above, in order to avoid bring too much complexity. |
Should we simply skip all values that don't respond to |
We should see what that breaks. I like the idea that |
b7a7701
to
5bf5a70
Compare
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.
Thanks for hanging in there 🍻 LGTM 🚀
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.
Thanks!
Is this a breaking change or a bug fix? If it's a breaking change, then we need to major-bump version 😱 . If it's a bug fix, then move the CHANGELOG line to fixes.
Either way please add a section to UPGRADING.md.
…at support `#length` method
5bf5a70
to
ba84149
Compare
I think this is a bug fix, because before, if parameters with types that don't support |
nil
in length
validatorlength
validator only takes effect for parameters with types that support #length
method
@ericproulx Could use a second opinion, please? |
Feels like a bug fix to me :). Nonetheless, I'm not sure about the UPGRADING comments since its a bug fix. |
I think better safe than sorry. Merging. |
Thanks to @dhruvCW for adding the length validator(#2437).
I found that in the case of optional parameters, passing in nil will lead to an exception. This behavior is not very friendly.
For nil, it should be a better choice to skip verification directly.