From 5555ff8a4969bc53cf5583db1b5c848d0b9f8487 Mon Sep 17 00:00:00 2001 From: Eric Proulx Date: Wed, 23 Sep 2026 08:51:26 +0200 Subject: [PATCH] Accept a beginless range of values for a bare Array type `requires :a, type: Array, values: ..5` raised `RangeError: cannot get the first element of beginless range` while the params were defined: `ValidationsSpec#guess_coerce_type` guessed the member type from `values.first`, which a beginless Range cannot answer. A Range now gives the class of its begin, or of its end when it has no begin. The incompatible-values check that follows still runs against that guess. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + lib/grape/validations/validations_spec.rb | 4 +++- spec/grape/validations/params_scope_spec.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ff174d36..ff5ddac3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ * [#2989](https://github.com/ruby-grape/grape/pull/2989): Leave the API format untouched when `format` rejects one with no content type - [@ericproulx](https://github.com/ericproulx). * [#2990](https://github.com/ruby-grape/grape/pull/2990): Name a nested array element by its own indices when a Hash scope sits between two Array scopes - [@ericproulx](https://github.com/ericproulx). * [#2991](https://github.com/ruby-grape/grape/pull/2991): Require exactly one name for a `requires` or `optional` block - [@ericproulx](https://github.com/ericproulx). +* [#2992](https://github.com/ruby-grape/grape/pull/2992): Accept a beginless range of `values` for a bare `Array` type - [@ericproulx](https://github.com/ericproulx). * Your contribution here. ### 4.0.1 (2026-09-15) diff --git a/lib/grape/validations/validations_spec.rb b/lib/grape/validations/validations_spec.rb index a23ff036c..f0a4aac30 100644 --- a/lib/grape/validations/validations_spec.rb +++ b/lib/grape/validations/validations_spec.rb @@ -154,7 +154,9 @@ def guess_coerce_type(coerce_type, *values_list) values_list.each do |values| next if !values || values.is_a?(Proc) - return values.first.class if values.is_a?(Range) || !values.empty? + # A beginless Range has no first element to ask. + return (values.begin || values.end).class if values.is_a?(Range) + return values.first.class unless values.empty? end coerce_type end diff --git a/spec/grape/validations/params_scope_spec.rb b/spec/grape/validations/params_scope_spec.rb index c1b0e491f..e0ad7e3bb 100644 --- a/spec/grape/validations/params_scope_spec.rb +++ b/spec/grape/validations/params_scope_spec.rb @@ -183,6 +183,21 @@ def initialize(value) end.not_to raise_error end + it 'accepts beginless and endless range values' do + expect do + subject.params do + requires :low, type: Array, values: ..5 + requires :high, type: Array, values: 1.. + end + end.not_to raise_error + end + + it 'still raises for a beginless range whose end is not of the member type' do + expect do + subject.params { requires :numbers, type: Array, values: ..5, except_values: ['a'] } + end.to raise_error Grape::Exceptions::IncompatibleOptionValues + end + it 'accepts an array containing only allowed values, given as a literal array' do subject.params do optional :periods, type: Array, values: %w[day month]