Skip to content

Commit a354dbd

Browse files
author
Joe Faber
committed
Add test_with option to values validator
1 parent 081d09b commit a354dbd

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#### Features
44

55
* [#1555](https://github.com/ruby-grape/grape/pull/1555): Added code coverage w/Coveralls - [@dblock](https://github.com/dblock).
6+
* [#1568](https://github.com/ruby-grape/grape/pull/1568): Add `test_with` option to `values` validator to allow custom checks - [@jlfaber](https://github.com/jlfaber).
67
* Your contribution here.
78

89
#### Fixes

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,20 @@ params do
11521152
end
11531153
```
11541154

1155+
Finally, for even greater control, an explicit validation Proc may be supplied using ```test_with```.
1156+
It will be called with a single argument (the input value), and should return
1157+
a truthy value if the value passes validation. If the input is an array, the Proc will be called
1158+
multiple times, once for each element in the array.
1159+
1160+
```ruby
1161+
params do
1162+
requires :number, type: Integer, values: { test_with: ->(v) { v.even? && v < 25 }, message: 'is odd or greater than 25' }
1163+
end
1164+
```
1165+
1166+
While ```test_with``` is convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1167+
1168+
11551169
#### `regexp`
11561170

11571171
Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value

lib/grape/validations/validators/values.rb

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ def initialize(attrs, options, required, scope, opts = {})
55
if options.is_a?(Hash)
66
@excepts = options[:except]
77
@values = options[:value]
8+
@test_with = options[:test_with]
9+
raise ArgumentError, 'test_with must be a Proc' if @test_with && !@test_with.is_a?(Proc)
810
else
911
@values = options
1012
end
@@ -24,6 +26,9 @@ def validate_param!(attr_name, params)
2426

2527
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
2628
if !values.nil? && !param_array.all? { |param| values.include?(param) }
29+
30+
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
31+
if @test_with && !param_array.all? { |param| @test_with.call(param) }
2732
end
2833

2934
private

spec/grape/validations/validators/values_spec.rb

+46
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,20 @@ class API < Grape::API
174174
optional :optional, type: Array[String], values: %w(a b c)
175175
end
176176
put '/optional_with_array_of_string_values'
177+
178+
params do
179+
requires :type, values: { test_with: ->(v) { ValuesModel.values.include? v } }
180+
end
181+
get '/test_with' do
182+
{ type: params[:type] }
183+
end
184+
185+
params do
186+
requires :type, values: { test_with: ->(v) { ValuesModel.values.include? v }, message: 'failed check' }
187+
end
188+
get '/test_with/message' do
189+
{ type: params[:type] }
190+
end
177191
end
178192
end
179193
end
@@ -505,4 +519,36 @@ def app
505519
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
506520
end
507521
end
522+
523+
context 'custom validation using test_with' do
524+
it 'accepts a single valid value' do
525+
get '/test_with', type: 'valid-type1'
526+
expect(last_response.status).to eq 200
527+
expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
528+
end
529+
530+
it 'accepts multiple valid values' do
531+
get '/test_with', type: ['valid-type1', 'valid-type3']
532+
expect(last_response.status).to eq 200
533+
expect(last_response.body).to eq({ type: ['valid-type1', 'valid-type3'] }.to_json)
534+
end
535+
536+
it 'rejects a single invalid value' do
537+
get '/test_with', type: 'invalid-type1'
538+
expect(last_response.status).to eq 400
539+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
540+
end
541+
542+
it 'rejects an invalid value among valid ones' do
543+
get '/test_with', type: ['valid-type1', 'invalid-type1', 'valid-type3']
544+
expect(last_response.status).to eq 400
545+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
546+
end
547+
548+
it 'uses supplied message' do
549+
get '/test_with/message', type: 'invalid-type1'
550+
expect(last_response.status).to eq 400
551+
expect(last_response.body).to eq({ error: 'type failed check' }.to_json)
552+
end
553+
end
508554
end

0 commit comments

Comments
 (0)