You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Make it possible to coerce nil Array of Types to [] #2068
Going back to our discussion in #2040 (comment), I've found it a bit tedious dealing with optional Array[Integer], Array[String], etc. types. Suppose I have an API with an optional parameter:
The first two cases are identical, but we have hundreds of API calls, and I'd rather not fix all the supporting code because nil is now a valid input.
For optional parameters, I can't use default: [] because this always includes the parameter, even though it's optional. Adding the default value will cause us to clear out the values when I only want to coerce the value if the parameter is provided.
In Grape v1.3.x, the coercion method is not run for nil, so I can't even manually coerce nil to some other value with a custom method. To upgrade to Grape v1.3.x, I have to do the coercion within the API handler with this helper function:
Thanks for bringing this up. Let me repeat this back to make sure I understand. Everything works except when the caller provides nil, in which case the coercer is not invoked and you don't get back [], but nil, and you want [] so that values can be cleared?
Attempting to change that by setting a default value to [] doesn't work either because if the caller provides nil, then the default value is not applied by design?
The behavior above is consistent for all types and we can't have it both ways. Personally, I don't think optionally allowing both is a great idea, it just creates a whole other matrix of possibilities. This leads me to think that easiest fix is actually in your API code itself by wrapping usage of values in Array(params[:values]), which turns nil and any single non-array parameter into an Array.
Attempting to change that by setting a default value to [] doesn't work either because if the caller provides nil, then the default value is not applied by design?
@dblock Thanks for responding. The problem is that if there is default, the parameter is always included, so if the caller doesn't provide the parameter there is unintended consequence of falling into case 2 instead of case 3 where values is left alone.
This leads me to think that easiest fix is actually in your API code itself by wrapping usage of values in Array(params[:values]), which turns nil and any single non-array parameter into an Array.
Yeah, I'd still have to do that for every optional parameter in every API call that I have. It's doable, but just a bit tedious and error-prone. I'd much prefer to push this up into the DSL with a custom type if necessary. It looks like parse is also not called if the value is nil.
Why do you want to clear all values when a parameter is not provided? Seems like the user of the API doesn't intend to do anything, then do nothing.
Note that the key is provided, but the value is nil. For example, both of these do the same thing, which clear out values:
PUT /myendpoint?values
PUT /myendpoint?values=
It's about preserving backwards compatibility since previous versions of Grape coerced nil to [], and I don't want to change the behavior of the above.
@dblock Thanks. The problem is that I'd have to do something like that for every Array type, and it's easy to miss. This is how I dealt with this automatically:
beforedocoerce_nil_params_to_array!end
And this is the helper:
# Grape v1.3.3 no longer automatically coerces an Array# type to an empty array if the value is nil.defcoerce_nil_params_to_array!keys_to_coerce=params_with_array_typesparams.eachdo |key,val|
params[key]=Array(val)ifval.nil? && keys_to_coerce.include?(key)endenddefparams_with_array_typesoptions[:route_options][:params].mapdo |key,val|
param_type=val[:type]# Search for parameters with Array types (e.g. "[String]", "[Integer]", etc.)ifparam_type =~ %r(\[\w*\])keyendend.compact.to_setend
I used types with NilClass optional :image_uids, types: [Array[String], NilClass], default: nil
If this works, should we be injecting NilClass every time the list of types has an Array and the keyword is optional? Someone wants to try to PR this and see whether any specs break?
Activity
dblock commentedon Jun 4, 2020
Thanks for bringing this up. Let me repeat this back to make sure I understand. Everything works except when the caller provides
nil, in which case the coercer is not invoked and you don't get back[], butnil, and you want[]so that values can be cleared?Attempting to change that by setting a default value to
[]doesn't work either because if the caller providesnil, then the default value is not applied by design?The behavior above is consistent for all types and we can't have it both ways. Personally, I don't think optionally allowing both is a great idea, it just creates a whole other matrix of possibilities. This leads me to think that easiest fix is actually in your API code itself by wrapping usage of values in
Array(params[:values]), which turnsniland any single non-array parameter into an Array.WDYT?
stanhu commentedon Jun 4, 2020
@dblock Thanks for responding. The problem is that if there is default, the parameter is always included, so if the caller doesn't provide the parameter there is unintended consequence of falling into case 2 instead of case 3 where
valuesis left alone.Yeah, I'd still have to do that for every optional parameter in every API call that I have. It's doable, but just a bit tedious and error-prone. I'd much prefer to push this up into the DSL with a custom type if necessary. It looks like
parseis also not called if the value isnil.dblock commentedon Jun 4, 2020
I see. Regarding the default that makes sense - there's a default value for when there's no value, no parameters, etc. It's ... the default.
Why do you want to clear all values when a parameter is not provided? Seems like the user of the API doesn't intend to do anything, then do nothing.
stanhu commentedon Jun 4, 2020
Note that the key is provided, but the value is
nil. For example, both of these do the same thing, which clear outvalues:PUT /myendpoint?valuesPUT /myendpoint?values=It's about preserving backwards compatibility since previous versions of Grape coerced
nilto[], and I don't want to change the behavior of the above.dblock commentedon Jun 4, 2020
For both of these, is
params.key?(:values) == trueand when the user doesn't includevaluesthat'sfalse? Then API could do this in abeforeblock?stanhu commentedon Jun 25, 2020
@dblock Thanks. The problem is that I'd have to do something like that for every Array type, and it's easy to miss. This is how I dealt with this automatically:
And this is the helper:
It feels like Grape should make this easier.
dblock commentedon Jun 25, 2020
I agree.
PervushinEugene commentedon Aug 4, 2021
I used types with NilClass
optional :image_uids, types: [Array[String], NilClass], default: nildblock commentedon Aug 4, 2021
If this works, should we be injecting
NilClassevery time the list oftypeshas anArrayand the keyword isoptional? Someone wants to try to PR this and see whether any specs break?