grape 1.5.1 → 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -3
- data/README.md +18 -6
- data/lib/grape.rb +1 -0
- data/lib/grape/api.rb +1 -1
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +1 -1
- data/lib/grape/dsl/parameters.rb +1 -1
- data/lib/grape/dsl/routing.rb +5 -4
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/base.rb +2 -2
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/stack.rb +12 -18
- data/lib/grape/validations/params_scope.rb +1 -1
- data/lib/grape/validations/types.rb +1 -4
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -0
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/validators/base.rb +5 -4
- data/lib/grape/validations/validators/coerce.rb +7 -4
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_remount_spec.rb +9 -4
- data/spec/grape/entity_spec.rb +1 -1
- data/spec/grape/middleware/stack_spec.rb +1 -2
- data/spec/grape/request_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +40 -12
- data/spec/grape/validations_spec.rb +2 -2
- data/spec/shared/versioning_examples.rb +20 -20
- data/spec/support/versioned_helpers.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac9fdc749f4dcad72fa8baacf0b07fa0fa499552521c789dd8e84439554ce275
|
4
|
+
data.tar.gz: 075b2bc7e75e7b0240973086cdae37cd2adf10ef4a4c419dc2cccd7a2741a3cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b845801e8c95ceee643853fd306c1513ce0611ebd535f3e2f058e95b4c491da08f58bcbd33b8d42d2cd8f1432d619dda203f8243fcc72e2b808ec4d0e15bc81c
|
7
|
+
data.tar.gz: ccdf098f65e4331fc35eaab564fbaf8d7411d1ae6f394f4cf4e0fdfa4f4871092e60b23a8a3a60ffc95680e3287446cac343932b0ef2af6b0745b91422d1d0a4
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,23 @@
|
|
1
|
+
### 1.5.2 (2021/02/06)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
* [#2157](https://github.com/ruby-grape/grape/pull/2157): Custom types can set a message to be used in the response when invalid - [@dnesteryuk](https://github.com/dnesteryuk).
|
6
|
+
* [#2145](https://github.com/ruby-grape/grape/pull/2145): Ruby 3.0 compatibility - [@ericproulx](https://github.com/ericproulx).
|
7
|
+
* [#2143](https://github.com/ruby-grape/grape/pull/2143): Enable GitHub Actions with updated RuboCop and Danger - [@anakinj](https://github.com/anakinj).
|
8
|
+
|
9
|
+
#### Fixes
|
10
|
+
|
11
|
+
* [#2144](https://github.com/ruby-grape/grape/pull/2144): Fix compatibility issue with activesupport 6.1 and XML serialization of arrays - [@anakinj](https://github.com/anakinj).
|
12
|
+
* [#2137](https://github.com/ruby-grape/grape/pull/2137): Fix typos - [@johnny-miyake](https://github.com/johnny-miyake).
|
13
|
+
* [#2131](https://github.com/ruby-grape/grape/pull/2131): Fix Ruby 2.7 keyword deprecation warning in validators/coerce - [@K0H205](https://github.com/K0H205).
|
14
|
+
* [#2132](https://github.com/ruby-grape/grape/pull/2132): Use #ruby2_keywords for correct delegation on Ruby <= 2.6, 2.7 and 3 - [@eregon](https://github.com/eregon).
|
15
|
+
* [#2152](https://github.com/ruby-grape/grape/pull/2152): Fix configuration method inside namespaced params - [@fsainz](https://github.com/fsainz).
|
16
|
+
|
1
17
|
### 1.5.1 (2020/11/15)
|
2
18
|
|
3
19
|
#### Fixes
|
4
20
|
|
5
|
-
* Your contribution here.
|
6
21
|
* [#2129](https://github.com/ruby-grape/grape/pull/2129): Fix validation error when Required Array nested inside an optional array, for Multiparam validators - [@dwhenry](https://github.com/dwhenry).
|
7
22
|
* [#2128](https://github.com/ruby-grape/grape/pull/2128): Fix validation error when Required Array nested inside an optional array - [@dwhenry](https://github.com/dwhenry).
|
8
23
|
* [#2127](https://github.com/ruby-grape/grape/pull/2127): Fix a performance issue with dependent params - [@dnesteryuk](https://github.com/dnesteryuk).
|
@@ -19,7 +34,7 @@
|
|
19
34
|
* [#2103](https://github.com/ruby-grape/grape/pull/2103): Ensure complete declared params structure is present - [@tlconnor](https://github.com/tlconnor).
|
20
35
|
* [#2099](https://github.com/ruby-grape/grape/pull/2099): Added truffleruby to Travis-CI - [@gogainda](https://github.com/gogainda).
|
21
36
|
* [#2089](https://github.com/ruby-grape/grape/pull/2089): Specify order of mounting Grape with Rack::Cascade in README - [@jonmchan](https://github.com/jonmchan).
|
22
|
-
* [#
|
37
|
+
* [#2088](https://github.com/ruby-grape/grape/pull/2088): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
|
23
38
|
* [#2092](https://github.com/ruby-grape/grape/pull/2092): Correct an example params in Include Missing doc - [@huyvohcmc](https://github.com/huyvohcmc).
|
24
39
|
* [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
|
25
40
|
* [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
|
@@ -622,7 +637,7 @@
|
|
622
637
|
* [#492](https://github.com/ruby-grape/grape/pull/492): Don't allow to have nil value when a param is required and has a list of allowed values - [@Antti](https://github.com/Antti).
|
623
638
|
* [#495](https://github.com/ruby-grape/grape/pull/495): Fixed `ParamsScope#params` for parameters nested inside arrays - [@asross](https://github.com/asross).
|
624
639
|
* [#498](https://github.com/ruby-grape/grape/pull/498): Dry'ed up options and headers logic, allow headers to be passed to OPTIONS requests - [@karlfreeman](https://github.com/karlfreeman).
|
625
|
-
* [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when
|
640
|
+
* [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when explicitly passed - [@yaneq](https://github.com/yaneq).
|
626
641
|
* [#503](https://github.com/ruby-grape/grape/pull/503): Calling declared(params) from child namespace fails to include parent namespace defined params - [@myitcv](https://github.com/myitcv).
|
627
642
|
* [#512](https://github.com/ruby-grape/grape/pull/512): Don't create `Grape::Request` multiple times - [@dblock](https://github.com/dblock).
|
628
643
|
* [#538](https://github.com/ruby-grape/grape/pull/538): Fixed default values for grouped params - [@dm1try](https://github.com/dm1try).
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
![grape logo](grape.png)
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/grape.svg)](http://badge.fury.io/rb/grape)
|
4
|
-
[![Build Status](https://
|
4
|
+
[![Build Status](https://github.com/ruby-grape/grape/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape/actions)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
|
7
7
|
[![Inline docs](https://inch-ci.org/github/ruby-grape/grape.svg)](https://inch-ci.org/github/ruby-grape/grape)
|
@@ -19,6 +19,8 @@
|
|
19
19
|
- [All](#all)
|
20
20
|
- [Rack](#rack)
|
21
21
|
- [ActiveRecord without Rails](#activerecord-without-rails)
|
22
|
+
- [Rails 4](#rails-4)
|
23
|
+
- [Rails 5+](#rails-5)
|
22
24
|
- [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
|
23
25
|
- [Rails](#rails)
|
24
26
|
- [Rails < 5.2](#rails--52)
|
@@ -156,7 +158,7 @@ content negotiation, versioning and much more.
|
|
156
158
|
|
157
159
|
## Stable Release
|
158
160
|
|
159
|
-
You're reading the documentation for the stable release of Grape, 1.5.
|
161
|
+
You're reading the documentation for the stable release of Grape, 1.5.2.
|
160
162
|
|
161
163
|
## Project Resources
|
162
164
|
|
@@ -315,13 +317,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
|
|
315
317
|
If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
|
316
318
|
is handled correctly.
|
317
319
|
|
320
|
+
#### Rails 4
|
321
|
+
|
318
322
|
The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
|
319
323
|
`config.ru` before mounting Grape, e.g.:
|
320
324
|
|
321
325
|
```ruby
|
322
326
|
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
327
|
+
```
|
323
328
|
|
324
|
-
|
329
|
+
#### Rails 5+
|
330
|
+
|
331
|
+
Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
use OTR::ActiveRecord::ConnectionManagement
|
325
335
|
```
|
326
336
|
|
327
337
|
### Alongside Sinatra (or other frameworks)
|
@@ -1171,7 +1181,8 @@ Aside from the default set of supported types listed above, any class can be
|
|
1171
1181
|
used as a type as long as an explicit coercion method is supplied. If the type
|
1172
1182
|
implements a class-level `parse` method, Grape will use it automatically.
|
1173
1183
|
This method must take one string argument and return an instance of the correct
|
1174
|
-
type, or
|
1184
|
+
type, or return an instance of `Grape::Types::InvalidValue` which optionally
|
1185
|
+
accepts a message to be returned in the response.
|
1175
1186
|
|
1176
1187
|
```ruby
|
1177
1188
|
class Color
|
@@ -1181,8 +1192,9 @@ class Color
|
|
1181
1192
|
end
|
1182
1193
|
|
1183
1194
|
def self.parse(value)
|
1184
|
-
|
1185
|
-
|
1195
|
+
return new(value) if %w[blue red green]).include?(value)
|
1196
|
+
|
1197
|
+
Grape::Types::InvalidValue.new('Unsupported color')
|
1186
1198
|
end
|
1187
1199
|
end
|
1188
1200
|
|
data/lib/grape.rb
CHANGED
@@ -12,6 +12,7 @@ require 'active_support/core_ext/hash/indifferent_access'
|
|
12
12
|
require 'active_support/core_ext/object/blank'
|
13
13
|
require 'active_support/core_ext/array/extract_options'
|
14
14
|
require 'active_support/core_ext/array/wrap'
|
15
|
+
require 'active_support/core_ext/array/conversions'
|
15
16
|
require 'active_support/core_ext/hash/deep_merge'
|
16
17
|
require 'active_support/core_ext/hash/reverse_merge'
|
17
18
|
require 'active_support/core_ext/hash/except'
|
data/lib/grape/api.rb
CHANGED
@@ -87,7 +87,7 @@ module Grape
|
|
87
87
|
end
|
88
88
|
|
89
89
|
# The remountable class can have a configuration hash to provide some dynamic class-level variables.
|
90
|
-
# For instance, a
|
90
|
+
# For instance, a description could be done using: `desc configuration[:description]` if it may vary
|
91
91
|
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
|
92
92
|
# too much, you may actually want to provide a new API rather than remount it.
|
93
93
|
def mount_instance(**opts)
|
data/lib/grape/dsl/callbacks.rb
CHANGED
@@ -59,7 +59,7 @@ module Grape
|
|
59
59
|
# end
|
60
60
|
# end
|
61
61
|
#
|
62
|
-
# This will make sure that the ApiLogger is opened and
|
62
|
+
# This will make sure that the ApiLogger is opened and closed around every
|
63
63
|
# request
|
64
64
|
# @param ensured_block [Proc] The block to be executed after every api_call
|
65
65
|
def finally(&block)
|
@@ -399,7 +399,7 @@ module Grape
|
|
399
399
|
entity_class = options.delete(:with)
|
400
400
|
|
401
401
|
if entity_class.nil?
|
402
|
-
# entity class not
|
402
|
+
# entity class not explicitly defined, auto-detect from relation#klass or first object in the collection
|
403
403
|
object_class = if object.respond_to?(:klass)
|
404
404
|
object.klass
|
405
405
|
else
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -72,7 +72,7 @@ module Grape
|
|
72
72
|
|
73
73
|
# Require one or more parameters for the current endpoint.
|
74
74
|
#
|
75
|
-
# @param attrs list of
|
75
|
+
# @param attrs list of parameters names, or, if :using is
|
76
76
|
# passed as an option, which keys to include (:all or :none) from
|
77
77
|
# the :using hash. The last key can be a hash, which specifies
|
78
78
|
# options for the parameters
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -79,11 +79,12 @@ module Grape
|
|
79
79
|
namespace_inheritable(:do_not_route_options, true)
|
80
80
|
end
|
81
81
|
|
82
|
-
def mount(mounts,
|
82
|
+
def mount(mounts, *opts)
|
83
83
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
84
84
|
mounts.each_pair do |app, path|
|
85
85
|
if app.respond_to?(:mount_instance)
|
86
|
-
|
86
|
+
opts_with = opts.any? ? opts.shift[:with] : {}
|
87
|
+
mount({ app.mount_instance(configuration: opts_with) => path })
|
87
88
|
next
|
88
89
|
end
|
89
90
|
in_setting = inheritable_setting
|
@@ -151,7 +152,7 @@ module Grape
|
|
151
152
|
end
|
152
153
|
|
153
154
|
# Declare a "namespace", which prefixes all subordinate routes with its
|
154
|
-
# name. Any endpoints within a namespace,
|
155
|
+
# name. Any endpoints within a namespace, group, resource or segment,
|
155
156
|
# etc., will share their parent context as well as any configuration
|
156
157
|
# done in the namespace context.
|
157
158
|
#
|
@@ -199,7 +200,7 @@ module Grape
|
|
199
200
|
@endpoints = []
|
200
201
|
end
|
201
202
|
|
202
|
-
#
|
203
|
+
# This method allows you to quickly define a parameter route segment
|
203
204
|
# in your API.
|
204
205
|
#
|
205
206
|
# @param param [Symbol] The name of the parameter you wish to declare.
|
@@ -17,7 +17,7 @@ module Grape
|
|
17
17
|
super(**args)
|
18
18
|
end
|
19
19
|
|
20
|
-
#
|
20
|
+
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
21
21
|
# and headers when converting a validation error to json or string
|
22
22
|
def as_json(*_args)
|
23
23
|
to_s
|
@@ -10,9 +10,9 @@ module Grape
|
|
10
10
|
|
11
11
|
attr_accessor :options, :app, :env
|
12
12
|
|
13
|
-
def initialize(app,
|
13
|
+
def initialize(app, *options)
|
14
14
|
@app = app
|
15
|
-
@options = options
|
15
|
+
@options = options.shift
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(env)
|
@@ -23,7 +23,7 @@ module Grape
|
|
23
23
|
self.env = env
|
24
24
|
|
25
25
|
if options.key?(:type)
|
26
|
-
auth_proc
|
26
|
+
auth_proc = options[:proc]
|
27
27
|
auth_proc_context = context
|
28
28
|
|
29
29
|
strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
|
@@ -15,9 +15,9 @@ module Grape
|
|
15
15
|
|
16
16
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
17
17
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
18
|
-
def initialize(app,
|
18
|
+
def initialize(app, *options)
|
19
19
|
@app = app
|
20
|
-
@options = default_options.merge(options)
|
20
|
+
@options = options.any? ? default_options.merge(options.shift) : default_options
|
21
21
|
@app_response = nil
|
22
22
|
end
|
23
23
|
|
@@ -6,12 +6,11 @@ module Grape
|
|
6
6
|
# It allows to insert and insert after
|
7
7
|
class Stack
|
8
8
|
class Middleware
|
9
|
-
attr_reader :args, :
|
9
|
+
attr_reader :args, :block, :klass
|
10
10
|
|
11
|
-
def initialize(klass, *args,
|
11
|
+
def initialize(klass, *args, &block)
|
12
12
|
@klass = klass
|
13
|
-
@args
|
14
|
-
@opts = opts
|
13
|
+
@args = args
|
15
14
|
@block = block
|
16
15
|
end
|
17
16
|
|
@@ -32,16 +31,8 @@ module Grape
|
|
32
31
|
klass.to_s
|
33
32
|
end
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
block ? builder.use(klass, *args, **opts, &block) : builder.use(klass, *args, **opts)
|
38
|
-
end
|
39
|
-
else
|
40
|
-
def use_in(builder)
|
41
|
-
args = self.args
|
42
|
-
args += [opts] unless opts.empty?
|
43
|
-
block ? builder.use(klass, *args, &block) : builder.use(klass, *args)
|
44
|
-
end
|
34
|
+
def use_in(builder)
|
35
|
+
builder.use(@klass, *@args, &@block)
|
45
36
|
end
|
46
37
|
end
|
47
38
|
|
@@ -70,11 +61,12 @@ module Grape
|
|
70
61
|
middlewares[i]
|
71
62
|
end
|
72
63
|
|
73
|
-
def insert(index, *args,
|
64
|
+
def insert(index, *args, &block)
|
74
65
|
index = assert_index(index, :before)
|
75
|
-
middleware = self.class::Middleware.new(*args,
|
66
|
+
middleware = self.class::Middleware.new(*args, &block)
|
76
67
|
middlewares.insert(index, middleware)
|
77
68
|
end
|
69
|
+
ruby2_keywords :insert if respond_to?(:ruby2_keywords, true)
|
78
70
|
|
79
71
|
alias insert_before insert
|
80
72
|
|
@@ -82,11 +74,13 @@ module Grape
|
|
82
74
|
index = assert_index(index, :after)
|
83
75
|
insert(index + 1, *args, &block)
|
84
76
|
end
|
77
|
+
ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
|
85
78
|
|
86
|
-
def use(*args,
|
87
|
-
middleware = self.class::Middleware.new(*args,
|
79
|
+
def use(*args, &block)
|
80
|
+
middleware = self.class::Middleware.new(*args, &block)
|
88
81
|
middlewares.push(middleware)
|
89
82
|
end
|
83
|
+
ruby2_keywords :use if respond_to?(:ruby2_keywords, true)
|
90
84
|
|
91
85
|
def merge_with(middleware_specs)
|
92
86
|
middleware_specs.each do |operation, *args|
|
@@ -7,6 +7,7 @@ require_relative 'types/multiple_type_coercer'
|
|
7
7
|
require_relative 'types/variant_collection_coercer'
|
8
8
|
require_relative 'types/json'
|
9
9
|
require_relative 'types/file'
|
10
|
+
require_relative 'types/invalid_value'
|
10
11
|
|
11
12
|
module Grape
|
12
13
|
module Validations
|
@@ -21,10 +22,6 @@ module Grape
|
|
21
22
|
# and {Grape::Dsl::Parameters#optional}. The main
|
22
23
|
# entry point for this process is {Types.build_coercer}.
|
23
24
|
module Types
|
24
|
-
# Instances of this class may be used as tokens to denote that
|
25
|
-
# a parameter value could not be coerced.
|
26
|
-
class InvalidValue; end
|
27
|
-
|
28
25
|
# Types representing a single value, which are coerced.
|
29
26
|
PRIMITIVES = [
|
30
27
|
# Numerical
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
module Types
|
6
|
+
# Instances of this class may be used as tokens to denote that a parameter value could not be
|
7
|
+
# coerced. The given message will be used as a validation error.
|
8
|
+
class InvalidValue
|
9
|
+
attr_reader :message
|
10
|
+
|
11
|
+
def initialize(message = nil)
|
12
|
+
@message = message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# only exists to make it shorter for external use
|
20
|
+
module Grape
|
21
|
+
module Types
|
22
|
+
InvalidValue = Class.new(Grape::Validations::Types::InvalidValue)
|
23
|
+
end
|
24
|
+
end
|
@@ -12,14 +12,15 @@ module Grape
|
|
12
12
|
# @param options [Object] implementation-dependent Validator options
|
13
13
|
# @param required [Boolean] attribute(s) are required or optional
|
14
14
|
# @param scope [ParamsScope] parent scope for this Validator
|
15
|
-
# @param opts [
|
16
|
-
def initialize(attrs, options, required, scope,
|
15
|
+
# @param opts [Array] additional validation options
|
16
|
+
def initialize(attrs, options, required, scope, *opts)
|
17
17
|
@attrs = Array(attrs)
|
18
18
|
@option = options
|
19
19
|
@required = required
|
20
20
|
@scope = scope
|
21
|
-
|
22
|
-
@
|
21
|
+
opts = opts.any? ? opts.shift : {}
|
22
|
+
@fail_fast = opts.fetch(:fail_fast, false)
|
23
|
+
@allow_blank = opts.fetch(:allow_blank, false)
|
23
24
|
end
|
24
25
|
|
25
26
|
# Validates a given request.
|
@@ -17,7 +17,7 @@ module Grape
|
|
17
17
|
|
18
18
|
module Validations
|
19
19
|
class CoerceValidator < Base
|
20
|
-
def initialize(
|
20
|
+
def initialize(attrs, options, required, scope, **opts)
|
21
21
|
super
|
22
22
|
|
23
23
|
@converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
@@ -36,7 +36,7 @@ module Grape
|
|
36
36
|
|
37
37
|
new_value = coerce_value(params[attr_name])
|
38
38
|
|
39
|
-
raise validation_exception(attr_name) unless valid_type?(new_value)
|
39
|
+
raise validation_exception(attr_name, new_value.message) unless valid_type?(new_value)
|
40
40
|
|
41
41
|
# Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
|
42
42
|
# which looses wrappers for hashes and arrays after reassigning values
|
@@ -80,8 +80,11 @@ module Grape
|
|
80
80
|
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
|
81
81
|
end
|
82
82
|
|
83
|
-
def validation_exception(attr_name)
|
84
|
-
Grape::Exceptions::Validation.new(
|
83
|
+
def validation_exception(attr_name, custom_msg = nil)
|
84
|
+
Grape::Exceptions::Validation.new(
|
85
|
+
params: [@scope.full_name(attr_name)],
|
86
|
+
message: custom_msg || message(:coerce)
|
87
|
+
)
|
85
88
|
end
|
86
89
|
end
|
87
90
|
end
|
data/lib/grape/version.rb
CHANGED
@@ -340,19 +340,24 @@ describe Grape::API do
|
|
340
340
|
context 'when the configuration is read within a namespace' do
|
341
341
|
before do
|
342
342
|
a_remounted_api.namespace 'api' do
|
343
|
+
params do
|
344
|
+
requires configuration[:required_param]
|
345
|
+
end
|
343
346
|
get "/#{configuration[:path]}" do
|
344
347
|
'10 votes'
|
345
348
|
end
|
346
349
|
end
|
347
|
-
root_api.mount a_remounted_api, with: { path: 'votes' }
|
348
|
-
root_api.mount a_remounted_api, with: { path: 'scores' }
|
350
|
+
root_api.mount a_remounted_api, with: { path: 'votes', required_param: 'param_key' }
|
351
|
+
root_api.mount a_remounted_api, with: { path: 'scores', required_param: 'param_key' }
|
349
352
|
end
|
350
353
|
|
351
354
|
it 'will use the dynamic configuration on all routes' do
|
352
|
-
get 'api/votes'
|
355
|
+
get 'api/votes', param_key: 'a'
|
353
356
|
expect(last_response.body).to eql '10 votes'
|
354
|
-
get 'api/scores'
|
357
|
+
get 'api/scores', param_key: 'a'
|
355
358
|
expect(last_response.body).to eql '10 votes'
|
359
|
+
get 'api/votes'
|
360
|
+
expect(last_response.status).to eq 400
|
356
361
|
end
|
357
362
|
end
|
358
363
|
|
data/spec/grape/entity_spec.rb
CHANGED
@@ -116,7 +116,7 @@ describe Grape::Entity do
|
|
116
116
|
expect(last_response.body).to eq('Auto-detect!')
|
117
117
|
end
|
118
118
|
|
119
|
-
it 'does not run autodetection for Entity when
|
119
|
+
it 'does not run autodetection for Entity when explicitly provided' do
|
120
120
|
entity = Class.new(Grape::Entity)
|
121
121
|
some_array = []
|
122
122
|
|
@@ -35,8 +35,7 @@ describe Grape::Middleware::Stack do
|
|
35
35
|
expect { subject.use StackSpec::BarMiddleware, false, my_arg: 42 }
|
36
36
|
.to change { subject.size }.by(1)
|
37
37
|
expect(subject.last).to eq(StackSpec::BarMiddleware)
|
38
|
-
expect(subject.last.args).to eq([false])
|
39
|
-
expect(subject.last.opts).to eq(my_arg: 42)
|
38
|
+
expect(subject.last.args).to eq([false, { my_arg: 42 }])
|
40
39
|
end
|
41
40
|
|
42
41
|
it 'pushes a middleware class with block arguments onto the stack' do
|
data/spec/grape/request_spec.rb
CHANGED
@@ -75,7 +75,7 @@ module Grape
|
|
75
75
|
Grape.config.reset
|
76
76
|
end
|
77
77
|
|
78
|
-
subject(:request_params) { Grape::Request.new(env, opts).params }
|
78
|
+
subject(:request_params) { Grape::Request.new(env, **opts).params }
|
79
79
|
|
80
80
|
context 'when the API does not include a specific param builder' do
|
81
81
|
let(:opts) { {} }
|
@@ -227,23 +227,51 @@ describe Grape::Validations::CoerceValidator do
|
|
227
227
|
expect(last_response.body).to eq('NilClass')
|
228
228
|
end
|
229
229
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
230
|
+
context 'a custom type' do
|
231
|
+
it 'coerces the given value' do
|
232
|
+
subject.params do
|
233
|
+
requires :uri, coerce: SecureURIOnly
|
234
|
+
end
|
235
|
+
subject.get '/secure_uri' do
|
236
|
+
params[:uri].class
|
237
|
+
end
|
238
|
+
|
239
|
+
get 'secure_uri', uri: 'https://www.example.com'
|
240
|
+
|
241
|
+
expect(last_response.status).to eq(200)
|
242
|
+
expect(last_response.body).to eq('URI::HTTPS')
|
243
|
+
|
244
|
+
get 'secure_uri', uri: 'http://www.example.com'
|
245
|
+
|
246
|
+
expect(last_response.status).to eq(400)
|
247
|
+
expect(last_response.body).to eq('uri is invalid')
|
236
248
|
end
|
237
249
|
|
238
|
-
|
250
|
+
context 'returning the InvalidValue instance when invalid' do
|
251
|
+
let(:custom_type) do
|
252
|
+
Class.new do
|
253
|
+
def self.parse(_val)
|
254
|
+
Grape::Types::InvalidValue.new('must be unique')
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
239
258
|
|
240
|
-
|
241
|
-
|
259
|
+
it 'uses a custom message added to the invalid value' do
|
260
|
+
type = custom_type
|
261
|
+
|
262
|
+
subject.params do
|
263
|
+
requires :name, type: type
|
264
|
+
end
|
265
|
+
subject.get '/whatever' do
|
266
|
+
params[:name].class
|
267
|
+
end
|
242
268
|
|
243
|
-
|
269
|
+
get 'whatever', name: 'Bob'
|
244
270
|
|
245
|
-
|
246
|
-
|
271
|
+
expect(last_response.status).to eq(400)
|
272
|
+
expect(last_response.body).to eq('name must be unique')
|
273
|
+
end
|
274
|
+
end
|
247
275
|
end
|
248
276
|
|
249
277
|
context 'Array' do
|
@@ -921,7 +921,7 @@ describe Grape::Validations do
|
|
921
921
|
expect(last_response.status).to eq(200)
|
922
922
|
end
|
923
923
|
|
924
|
-
it "simplest example using
|
924
|
+
it "simplest example using Array -> Array -> Hash -> String" do
|
925
925
|
subject.params do
|
926
926
|
requires :orders, type: Array do
|
927
927
|
requires :id, type: Integer
|
@@ -947,7 +947,7 @@ describe Grape::Validations do
|
|
947
947
|
expect(last_response.status).to eq(200)
|
948
948
|
end
|
949
949
|
|
950
|
-
it "simplest example using
|
950
|
+
it "simplest example using Array -> Hash -> String" do
|
951
951
|
subject.params do
|
952
952
|
requires :orders, type: Array do
|
953
953
|
requires :id, type: Integer
|
@@ -7,7 +7,7 @@ shared_examples_for 'versioning' do
|
|
7
7
|
subject.get :hello do
|
8
8
|
"Version: #{request.env['api.version']}"
|
9
9
|
end
|
10
|
-
versioned_get '/hello', 'v1', macro_options
|
10
|
+
versioned_get '/hello', 'v1', **macro_options
|
11
11
|
expect(last_response.body).to eql 'Version: v1'
|
12
12
|
end
|
13
13
|
|
@@ -18,7 +18,7 @@ shared_examples_for 'versioning' do
|
|
18
18
|
subject.get :hello do
|
19
19
|
"Version: #{request.env['api.version']}"
|
20
20
|
end
|
21
|
-
versioned_get '/hello', 'v1', macro_options.merge(prefix: 'api')
|
21
|
+
versioned_get '/hello', 'v1', **macro_options.merge(prefix: 'api')
|
22
22
|
expect(last_response.body).to eql 'Version: v1'
|
23
23
|
end
|
24
24
|
|
@@ -34,14 +34,14 @@ shared_examples_for 'versioning' do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
versioned_get '/awesome', 'v1', macro_options
|
37
|
+
versioned_get '/awesome', 'v1', **macro_options
|
38
38
|
expect(last_response.status).to eql 404
|
39
39
|
|
40
|
-
versioned_get '/awesome', 'v2', macro_options
|
40
|
+
versioned_get '/awesome', 'v2', **macro_options
|
41
41
|
expect(last_response.status).to eql 200
|
42
|
-
versioned_get '/legacy', 'v1', macro_options
|
42
|
+
versioned_get '/legacy', 'v1', **macro_options
|
43
43
|
expect(last_response.status).to eql 200
|
44
|
-
versioned_get '/legacy', 'v2', macro_options
|
44
|
+
versioned_get '/legacy', 'v2', **macro_options
|
45
45
|
expect(last_response.status).to eql 404
|
46
46
|
end
|
47
47
|
|
@@ -51,11 +51,11 @@ shared_examples_for 'versioning' do
|
|
51
51
|
'I exist'
|
52
52
|
end
|
53
53
|
|
54
|
-
versioned_get '/awesome', 'v1', macro_options
|
54
|
+
versioned_get '/awesome', 'v1', **macro_options
|
55
55
|
expect(last_response.status).to eql 200
|
56
|
-
versioned_get '/awesome', 'v2', macro_options
|
56
|
+
versioned_get '/awesome', 'v2', **macro_options
|
57
57
|
expect(last_response.status).to eql 200
|
58
|
-
versioned_get '/awesome', 'v3', macro_options
|
58
|
+
versioned_get '/awesome', 'v3', **macro_options
|
59
59
|
expect(last_response.status).to eql 404
|
60
60
|
end
|
61
61
|
|
@@ -74,10 +74,10 @@ shared_examples_for 'versioning' do
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
versioned_get '/version', 'v2', macro_options
|
77
|
+
versioned_get '/version', 'v2', **macro_options
|
78
78
|
expect(last_response.status).to eq(200)
|
79
79
|
expect(last_response.body).to eq('v2')
|
80
|
-
versioned_get '/version', 'v1', macro_options
|
80
|
+
versioned_get '/version', 'v1', **macro_options
|
81
81
|
expect(last_response.status).to eq(200)
|
82
82
|
expect(last_response.body).to eq('version v1')
|
83
83
|
end
|
@@ -98,11 +98,11 @@ shared_examples_for 'versioning' do
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
versioned_get '/version', 'v1', macro_options.merge(prefix: subject.prefix)
|
101
|
+
versioned_get '/version', 'v1', **macro_options.merge(prefix: subject.prefix)
|
102
102
|
expect(last_response.status).to eq(200)
|
103
103
|
expect(last_response.body).to eq('version v1')
|
104
104
|
|
105
|
-
versioned_get '/version', 'v2', macro_options.merge(prefix: subject.prefix)
|
105
|
+
versioned_get '/version', 'v2', **macro_options.merge(prefix: subject.prefix)
|
106
106
|
expect(last_response.status).to eq(200)
|
107
107
|
expect(last_response.body).to eq('v2')
|
108
108
|
end
|
@@ -131,11 +131,11 @@ shared_examples_for 'versioning' do
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
-
versioned_get '/version', 'v1', macro_options.merge(prefix: subject.prefix)
|
134
|
+
versioned_get '/version', 'v1', **macro_options.merge(prefix: subject.prefix)
|
135
135
|
expect(last_response.status).to eq(200)
|
136
136
|
expect(last_response.body).to eq('v1-version')
|
137
137
|
|
138
|
-
versioned_get '/version', 'v2', macro_options.merge(prefix: subject.prefix)
|
138
|
+
versioned_get '/version', 'v2', **macro_options.merge(prefix: subject.prefix)
|
139
139
|
expect(last_response.status).to eq(200)
|
140
140
|
expect(last_response.body).to eq('v2-version')
|
141
141
|
end
|
@@ -148,7 +148,7 @@ shared_examples_for 'versioning' do
|
|
148
148
|
subject.get :api_version_with_version_param do
|
149
149
|
params[:version]
|
150
150
|
end
|
151
|
-
versioned_get '/api_version_with_version_param?version=1', 'v1', macro_options
|
151
|
+
versioned_get '/api_version_with_version_param?version=1', 'v1', **macro_options
|
152
152
|
expect(last_response.body).to eql '1'
|
153
153
|
end
|
154
154
|
|
@@ -183,13 +183,13 @@ shared_examples_for 'versioning' do
|
|
183
183
|
|
184
184
|
context 'v1' do
|
185
185
|
it 'finds endpoint' do
|
186
|
-
versioned_get '/version', 'v1', macro_options
|
186
|
+
versioned_get '/version', 'v1', **macro_options
|
187
187
|
expect(last_response.status).to eq(200)
|
188
188
|
expect(last_response.body).to eq('v1')
|
189
189
|
end
|
190
190
|
|
191
191
|
it 'finds catch all' do
|
192
|
-
versioned_get '/whatever', 'v1', macro_options
|
192
|
+
versioned_get '/whatever', 'v1', **macro_options
|
193
193
|
expect(last_response.status).to eq(200)
|
194
194
|
expect(last_response.body).to end_with 'whatever'
|
195
195
|
end
|
@@ -197,13 +197,13 @@ shared_examples_for 'versioning' do
|
|
197
197
|
|
198
198
|
context 'v2' do
|
199
199
|
it 'finds endpoint' do
|
200
|
-
versioned_get '/version', 'v2', macro_options
|
200
|
+
versioned_get '/version', 'v2', **macro_options
|
201
201
|
expect(last_response.status).to eq(200)
|
202
202
|
expect(last_response.body).to eq('v2')
|
203
203
|
end
|
204
204
|
|
205
205
|
it 'finds catch all' do
|
206
|
-
versioned_get '/whatever', 'v2', macro_options
|
206
|
+
versioned_get '/whatever', 'v2', **macro_options
|
207
207
|
expect(last_response.status).to eq(200)
|
208
208
|
expect(last_response.body).to end_with 'whatever'
|
209
209
|
end
|
@@ -44,7 +44,7 @@ module Spec
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def versioned_get(path, version_name, **version_options)
|
47
|
-
path
|
47
|
+
path = versioned_path(**version_options.merge(version: version_name, path: path))
|
48
48
|
headers = versioned_headers(**version_options.merge(version: version_name))
|
49
49
|
params = {}
|
50
50
|
params = { version_options[:parameter] => version_name } if version_options[:using] == :param
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -227,6 +227,7 @@ files:
|
|
227
227
|
- lib/grape/validations/types/custom_type_collection_coercer.rb
|
228
228
|
- lib/grape/validations/types/dry_type_coercer.rb
|
229
229
|
- lib/grape/validations/types/file.rb
|
230
|
+
- lib/grape/validations/types/invalid_value.rb
|
230
231
|
- lib/grape/validations/types/json.rb
|
231
232
|
- lib/grape/validations/types/multiple_type_coercer.rb
|
232
233
|
- lib/grape/validations/types/primitive_coercer.rb
|
@@ -369,9 +370,9 @@ licenses:
|
|
369
370
|
- MIT
|
370
371
|
metadata:
|
371
372
|
bug_tracker_uri: https://github.com/ruby-grape/grape/issues
|
372
|
-
changelog_uri: https://github.com/ruby-grape/grape/blob/v1.5.
|
373
|
-
documentation_uri: https://www.rubydoc.info/gems/grape/1.5.
|
374
|
-
source_code_uri: https://github.com/ruby-grape/grape/tree/v1.5.
|
373
|
+
changelog_uri: https://github.com/ruby-grape/grape/blob/v1.5.2/CHANGELOG.md
|
374
|
+
documentation_uri: https://www.rubydoc.info/gems/grape/1.5.2
|
375
|
+
source_code_uri: https://github.com/ruby-grape/grape/tree/v1.5.2
|
375
376
|
post_install_message:
|
376
377
|
rdoc_options: []
|
377
378
|
require_paths:
|
@@ -387,7 +388,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
387
388
|
- !ruby/object:Gem::Version
|
388
389
|
version: '0'
|
389
390
|
requirements: []
|
390
|
-
rubygems_version: 3.
|
391
|
+
rubygems_version: 3.1.2
|
391
392
|
signing_key:
|
392
393
|
specification_version: 4
|
393
394
|
summary: A simple Ruby framework for building REST-like APIs.
|