Skip to content

Commit

Permalink
Rejigger terminology and add explicit support for contract class
Browse files Browse the repository at this point in the history
And add a test for that.

ruby-grape#2386
  • Loading branch information
dgutov committed Mar 21, 2024
1 parent c3e87be commit c784875
Show file tree
Hide file tree
Showing 16 changed files with 69 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gemspec
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2091,7 +2091,7 @@ end

As an alternative to the `params` DSL described above, you can use a schema or `dry-validation` contract to describe an endpoint's parameters. This can be especially useful if you use the above already in some other parts of your application.

Call `contract` with a schema defined previously
Call `contract` with a contract or schema defined previously

```rb
contract CreateFoosSchema
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/multi_json.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'multi_json', require: 'multi_json'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/multi_xml.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'multi_xml', require: 'multi_xml'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
1 change: 1 addition & 0 deletions gemfiles/no_dry_schema.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ source 'https://rubygems.org'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rack_1_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rack', '~> 1.0'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rack_2_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rack', '~> 2.0'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rack_3_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rack', '~> 3.0.0'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rack_edge.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rack', github: 'rack/rack'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails_6_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rails', '~> 6.0.0'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails_6_1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rails', '~> 6.1'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails_7_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rails', '~> 7.0.0'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails_edge.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ gem 'rails', github: 'rails/rails'
group :development, :test do
gem 'bundler'
gem 'hashie'
gem 'dry-schema'
gem 'dry-validation'
gem 'rake'
gem 'rubocop', '1.59.0', require: false
gem 'rubocop-performance', '1.20.1', require: false
Expand Down
13 changes: 7 additions & 6 deletions lib/grape/dsl/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,16 @@ def params(&block)
end

# Declare the contract to be used for the endpoint's parameters.
# @param klass [Class<Dry::Validation::Contract> | Class<Dry::Schema::Processor>]
# The contract or schema class to be used for validation. Optional.
# @param contract [Class<Dry::Validation::Contract> | Dry::Schema::Processor]
# The contract or schema to be used for validation. Optional.
# @yield a block yielding a new instance of Dry::Schema::Params
# subclass, allowing to define the schema inline. When the
# +klass+ parameter is non-nil, it will be used as a parent. Optional.
def contract(klass = nil, &block)
raise ArgumentError, 'Either klass or block must be provided' unless klass || block
# +contract+ parameter is a schema, it will be used as a parent. Optional.
def contract(contract = nil, &block)
raise ArgumentError, 'Either contract or block must be provided' unless contract || block
raise ArgumentError, 'Cannot inherit from contract, only schema' if block && contract.respond_to?(:schema)

Grape::Validations::ContractScope.new(self, klass, &block)
Grape::Validations::ContractScope.new(self, contract, &block)
end
end
end
Expand Down
20 changes: 15 additions & 5 deletions lib/grape/validations/contract_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ module Validations
class ContractScope
# Declare the contract to be used for the endpoint's parameters.
# @param api [API] the API endpoint to modify.
# @param klass [Class] contract or schema class to be used for validation. Optional.
# @param contract the contract or schema to be used for validation. Optional.
# @yield a block yielding a new schema class. Optional.
def initialize(api, klass = nil, &block)
klass = Dry::Schema.Params(parent: klass, &block) if block
def initialize(api, contract = nil, &block)
# When block is passed, the first arg is either schema or nil.
contract = Dry::Schema.Params(parent: contract, &block) if block

api.namespace_stackable(:contract_key_map, klass.key_map)
if contract.respond_to?(:new)
# It's a Dry::Validation::Contract, then.
contract = contract.new
key_map = contract.schema.key_map
else
# It's Dry::Schema::Processor.
key_map = contract.key_map
end

api.namespace_stackable(:contract_key_map, key_map)

validator_options = {
validator_class: Validator,
opts: { schema: klass }
opts: { schema: contract }
}

api.namespace_stackable(:validations, validator_options)
Expand Down
36 changes: 34 additions & 2 deletions spec/grape/validations/contract_scope_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
end

context 'with simple schema, pre-defined' do
let(:schema) do
let(:contract) do
Dry::Schema.Params do
required(:number).filled(:integer)
end
end

before do
app.contract(schema)
app.contract(contract)
app.post('/required')
end

Expand All @@ -39,6 +39,38 @@
end
end

context 'with contract class' do
let(:contract) do
Class.new(Dry::Validation::Contract) do
params do
required(:number).filled(:integer)
required(:name).filled(:string)
end

rule(:number) do
key.failure('is too high') if value > 5
end
end
end

before do
app.contract(contract)
app.post('/required')
end

it 'coerces the parameter' do
post '/required', number: '1', name: '2'
expect(last_response.status).to eq(201)
expect(validated_params).to eq('number' => 1, 'name' => '2')
end

it 'shows expected validation error' do
post '/required', number: '6'
expect(last_response.status).to eq(400)
expect(last_response.body).to eq('name is missing, number is too high')
end
end

context 'with nested schema' do
before do
app.contract do
Expand Down

0 comments on commit c784875

Please sign in to comment.