Skip to content

Commit 12dc739

Browse files
authored
Add is: parameter to the length validator (#2485)
1 parent 04e69ea commit 12dc739

File tree

5 files changed

+82
-3
lines changed

5 files changed

+82
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* [#2478](https://github.com/ruby-grape/grape/pull/2478): Fix rescue_from with invalid response - [@ericproulx](https://github.com/ericproulx).
1313
* [#2480](https://github.com/ruby-grape/grape/pull/2480): Fix rescue_from ValidationErrors exception - [@numbata](https://github.com/numbata).
1414
* [#2464](https://github.com/ruby-grape/grape/pull/2464): The `length` validator only takes effect for parameters with types that support `#length` method - [@OuYangJinTing](https://github.com/OuYangJinTing).
15+
* [#2485](https://github.com/ruby-grape/grape/pull/2485): Add `is:` param to length validator - [@dakad](https://github.com/dakad).
1516
* Your contribution here.
1617

1718
### 2.1.3 (2024-07-13)

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1714,10 +1714,11 @@ end
17141714

17151715
Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.
17161716

1717-
The validator accepts `:min` or `:max` or both options to validate that the value of the parameter is within the given limits.
1717+
The validator accepts `:min` or `:max` or both options or only `:is` to validate that the value of the parameter is within the given limits.
17181718

17191719
```ruby
17201720
params do
1721+
requires :code, type: String, length: { is: 2 }
17211722
requires :str, type: String, length: { min: 3 }
17221723
requires :list, type: [Integer], length: { min: 3, max: 5 }
17231724
requires :hash, type: Hash, length: { max: 5 }
@@ -2045,6 +2046,7 @@ end
20452046

20462047
```ruby
20472048
params do
2049+
requires :code, type: String, length: { is: 2, message: 'code is expected to be exactly 2 characters long' }
20482050
requires :str, type: String, length: { min: 5, message: 'str is expected to be atleast 5 characters long' }
20492051
requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
20502052
end

lib/grape/locale/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ en:
1111
except_values: 'has a value not allowed'
1212
same_as: 'is not the same as %{parameter}'
1313
length: 'is expected to have length within %{min} and %{max}'
14+
length_is: 'is expected to have length exactly equal to %{is}'
1415
length_min: 'is expected to have length greater than or equal to %{min}'
1516
length_max: 'is expected to have length less than or equal to %{max}'
1617
missing_vendor_option:

lib/grape/validations/validators/length_validator.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,25 @@ class LengthValidator < Base
77
def initialize(attrs, options, required, scope, **opts)
88
@min = options[:min]
99
@max = options[:max]
10+
@is = options[:is]
1011

1112
super
1213

1314
raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
1415
raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
1516
raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
17+
18+
return if @is.nil?
19+
raise ArgumentError, 'is must be an integer greater than zero' if !@is.is_a?(Integer) || !@is.positive?
20+
raise ArgumentError, 'is cannot be combined with min or max' if !@min.nil? || !@max.nil?
1621
end
1722

1823
def validate_param!(attr_name, params)
1924
param = params[attr_name]
2025

2126
return unless param.respond_to?(:length)
2227

23-
return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max)
28+
return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
2429

2530
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
2631
end
@@ -32,8 +37,10 @@ def build_message
3237
format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
3338
elsif @min
3439
format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
35-
else
40+
elsif @max
3641
format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
42+
else
43+
format I18n.t(:length_is, scope: 'grape.errors.messages'), is: @is
3744
end
3845
end
3946
end

spec/grape/validations/validators/length_spec.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,24 @@
8686
end
8787
post '/custom-message' do
8888
end
89+
90+
params do
91+
requires :code, length: { is: 2 }
92+
end
93+
post 'is' do
94+
end
95+
96+
params do
97+
requires :code, length: { is: -2 }
98+
end
99+
post 'negative_is' do
100+
end
101+
102+
params do
103+
requires :code, length: { is: 2, max: 10 }
104+
end
105+
post 'is_with_max' do
106+
end
89107
end
90108
end
91109

@@ -298,4 +316,54 @@
298316
end
299317
end
300318
end
319+
320+
describe '/is' do
321+
context 'when length is exact' do
322+
it do
323+
post 'is', code: 'ZZ'
324+
expect(last_response.status).to eq(201)
325+
expect(last_response.body).to eq('')
326+
end
327+
end
328+
329+
context 'when length exceeds the limit' do
330+
it do
331+
post 'is', code: 'aze'
332+
expect(last_response.status).to eq(400)
333+
expect(last_response.body).to eq('code is expected to have length exactly equal to 2')
334+
end
335+
end
336+
337+
context 'when length is less than the limit' do
338+
it do
339+
post 'is', code: 'a'
340+
expect(last_response.status).to eq(400)
341+
expect(last_response.body).to eq('code is expected to have length exactly equal to 2')
342+
end
343+
end
344+
345+
context 'when length is zero' do
346+
it do
347+
post 'is', code: ''
348+
expect(last_response.status).to eq(400)
349+
expect(last_response.body).to eq('code is expected to have length exactly equal to 2')
350+
end
351+
end
352+
end
353+
354+
describe '/negative_is' do
355+
context 'when `is` is negative' do
356+
it do
357+
expect { post 'negative_is', code: 'ZZ' }.to raise_error(ArgumentError, 'is must be an integer greater than zero')
358+
end
359+
end
360+
end
361+
362+
describe '/is_with_max' do
363+
context 'when `is` is combined with max' do
364+
it do
365+
expect { post 'is_with_max', code: 'ZZ' }.to raise_error(ArgumentError, 'is cannot be combined with min or max')
366+
end
367+
end
368+
end
301369
end

0 commit comments

Comments
 (0)