Skip to content

Commit

Permalink
Add ability to use Grape::Entity documentation in the params block.
Browse files Browse the repository at this point in the history
  • Loading branch information
Reynard Hilman authored and dblock committed Jan 30, 2014
1 parent 3be5c01 commit f5d7306
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Next Release
* [#544](https://github.com/intridea/grape/pull/544): The `rescue_from` keyword now handles subclasses of exceptions by default - [@xevix](https://github.com/xevix).
* [#545](https://github.com/intridea/grape/pull/545): Added `type` (Array or Hash) support to `requires`, `optional` and `group` - [@bwalex](https://github.com/bwalex).
* [#550](https://github.com/intridea/grape/pull/550): Added possibility to define reusable params - [@dm1try](https://github.com/dm1try).
* [#560](https://github.com/intridea/grape/pull/560): Use `Grape::Entity` documentation to define required and optional parameters with `requires using:` - [@reynardmh](https://github.com/reynardmh).
* Your contribution here.

#### Fixes
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,6 @@ The following example exposes statuses.

```ruby
module API

module Entities
class Status < Grape::Entity
expose :user_name
Expand All @@ -1196,6 +1195,23 @@ module API
end
```

You can use entity documentation directly in the params block with `using: Entity.documentation`.

```ruby
module API
class Statuses < Grape::API
version 'v1'

desc 'Create a status', {
requires :all, except: [:ip], using: API::Entities::Status.documentation.except(:id)
}
post '/status' do
Status.create! params
end
end
end
```

You can present with multiple entities using an optional Symbol argument.

```ruby
Expand Down
38 changes: 32 additions & 6 deletions lib/grape/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,16 @@ def should_validate?(parameters)
def requires(*attrs, &block)
orig_attrs = attrs.clone

validations = { presence: true }
validations.merge!(attrs.pop) if attrs.last.is_a?(Hash)
validations[:type] ||= Array if block_given?
validates(attrs, validations)
opts = attrs.last.is_a?(Hash) ? attrs.pop : nil

block_given? ? new_scope(orig_attrs, &block) :
push_declared_params(attrs)
if opts && opts[:using]
require_required_and_optional_fields(attrs.first, opts)
else
validate_attributes(attrs, opts, &block)

block_given? ? new_scope(orig_attrs, &block) :
push_declared_params(attrs)
end
end

def optional(*attrs, &block)
Expand Down Expand Up @@ -172,6 +175,29 @@ def push_declared_params(attrs)

private

def require_required_and_optional_fields(context, opts)
if context == :all
optional_fields = opts[:except].is_a?(Array) ? opts[:except] : [opts[:except]].compact
required_fields = opts[:using].keys - optional_fields
else # attrs.first == :none
required_fields = opts[:except].is_a?(Array) ? opts[:except] : [opts[:except]].compact
optional_fields = opts[:using].keys - required_fields
end
required_fields.each do |field|
requires(field, opts[:using][field])
end
optional_fields.each do |field|
optional(field, opts[:using][field])
end
end

def validate_attributes(attrs, opts, &block)
validations = { presence: true }
validations.merge!(opts) if opts
validations[:type] ||= Array if block
validates(attrs, validations)
end

def new_scope(attrs, optional = false, &block)
opts = attrs[1] || { type: Array }
raise ArgumentError unless opts.keys.to_set.subset? [:type].to_set
Expand Down
70 changes: 70 additions & 0 deletions spec/grape/validations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,76 @@ def app
end
end

context 'requires :all using Grape::Entity documentation' do
def define_requires_all
documentation = {
required_field: { type: String },
optional_field: { type: String }
}
subject.params do
requires :all, except: :optional_field, using: documentation
end
end
before do
define_requires_all
subject.get '/required' do
'required works'
end
end

it 'adds entity documentation to declared params' do
define_requires_all
subject.settings[:declared_params].should == [:required_field, :optional_field]
end

it 'errors when required_field is not present' do
get '/required'
last_response.status.should == 400
last_response.body.should == 'required_field is missing'
end

it 'works when required_field is present' do
get '/required', required_field: 'woof'
last_response.status.should == 200
last_response.body.should == 'required works'
end
end

context 'requires :none using Grape::Entity documentation' do
def define_requires_none
documentation = {
required_field: { type: String },
optional_field: { type: String }
}
subject.params do
requires :none, except: :required_field, using: documentation
end
end
before do
define_requires_none
subject.get '/required' do
'required works'
end
end

it 'adds entity documentation to declared params' do
define_requires_none
subject.settings[:declared_params].should == [:required_field, :optional_field]
end

it 'errors when required_field is not present' do
get '/required'
last_response.status.should == 400
last_response.body.should == 'required_field is missing'
end

it 'works when required_field is present' do
get '/required', required_field: 'woof'
last_response.status.should == 200
last_response.body.should == 'required works'
end
end

context 'required with an Array block' do
before do
subject.params do
Expand Down

0 comments on commit f5d7306

Please sign in to comment.