Skip to content

Commit

Permalink
Merge pull request #1568 from jlfaber/check_with
Browse files Browse the repository at this point in the history
Add proc option to values validator
  • Loading branch information
dblock authored Feb 1, 2017
2 parents 081d09b + 5d520ba commit d124df7
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### Features

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

#### Fixes
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,20 @@ params do
end
```

Finally, for even greater control, an explicit validation Proc may be supplied using ```proc```.
It will be called with a single argument (the input value), and should return
a truthy value if the value passes validation. If the input is an array, the Proc will be called
multiple times, once for each element in the array.

```ruby
params do
requires :number, type: Integer, values: { proc: ->(v) { v.even? && v < 25 }, message: 'is odd or greater than 25' }
end
```

While ```proc``` is convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.


#### `regexp`

Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
Expand Down
5 changes: 5 additions & 0 deletions lib/grape/validations/validators/values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ def initialize(attrs, options, required, scope, opts = {})
if options.is_a?(Hash)
@excepts = options[:except]
@values = options[:value]
@proc = options[:proc]
raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
else
@values = options
end
Expand All @@ -24,6 +26,9 @@ def validate_param!(attr_name, params)

raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
if !values.nil? && !param_array.all? { |param| values.include?(param) }

raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
if @proc && !param_array.all? { |param| @proc.call(param) }
end

private
Expand Down
56 changes: 47 additions & 9 deletions spec/grape/validations/validators/values_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,17 @@ class API < Grape::API
params do
requires :type, values: { except: ValuesModel.excepts, except_message: 'value is on exclusions list', message: 'default exclude message' }
end
get '/exclude/exclude_message' do
{ type: params[:type] }
end
get '/exclude/exclude_message'

params do
requires :type, values: { except: -> { ValuesModel.excepts }, except_message: 'value is on exclusions list' }
end
get '/exclude/lambda/exclude_message' do
{ type: params[:type] }
end
get '/exclude/lambda/exclude_message'

params do
requires :type, values: { except: ValuesModel.excepts, message: 'default exclude message' }
end
get '/exclude/fallback_message' do
{ type: params[:type] }
end
get '/exclude/fallback_message'
end

params do
Expand Down Expand Up @@ -174,6 +168,18 @@ class API < Grape::API
optional :optional, type: Array[String], values: %w(a b c)
end
put '/optional_with_array_of_string_values'

params do
requires :type, values: { proc: ->(v) { ValuesModel.values.include? v } }
end
get '/proc' do
{ type: params[:type] }
end

params do
requires :type, values: { proc: ->(v) { ValuesModel.values.include? v }, message: 'failed check' }
end
get '/proc/message'
end
end
end
Expand Down Expand Up @@ -505,4 +511,36 @@ def app
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
end
end

context 'custom validation using proc' do
it 'accepts a single valid value' do
get '/proc', type: 'valid-type1'
expect(last_response.status).to eq 200
expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
end

it 'accepts multiple valid values' do
get '/proc', type: ['valid-type1', 'valid-type3']
expect(last_response.status).to eq 200
expect(last_response.body).to eq({ type: ['valid-type1', 'valid-type3'] }.to_json)
end

it 'rejects a single invalid value' do
get '/proc', type: 'invalid-type1'
expect(last_response.status).to eq 400
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
end

it 'rejects an invalid value among valid ones' do
get '/proc', type: ['valid-type1', 'invalid-type1', 'valid-type3']
expect(last_response.status).to eq 400
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
end

it 'uses supplied message' do
get '/proc/message', type: 'invalid-type1'
expect(last_response.status).to eq 400
expect(last_response.body).to eq({ error: 'type failed check' }.to_json)
end
end
end

0 comments on commit d124df7

Please sign in to comment.