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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc2b398e9ad95e7e4bb2641bbdaada929d992cd50fb551b56dbafe5f03a75589
4
- data.tar.gz: 03765fbe9d5dafb28b531f2a454f06b2ac59d16ea28691036de797b8e64ced06
3
+ metadata.gz: ac9fdc749f4dcad72fa8baacf0b07fa0fa499552521c789dd8e84439554ce275
4
+ data.tar.gz: 075b2bc7e75e7b0240973086cdae37cd2adf10ef4a4c419dc2cccd7a2741a3cd
5
5
  SHA512:
6
- metadata.gz: a078c1c951ff6e5dfee34c78ab04e1f2866be316af23de469db12301ec3c86eb53bcdc9c8ac478aabe02bd3dc34f4772166e49bdf85ef731a051a8d0de73441f
7
- data.tar.gz: 7146c097cfced8308f1efb314fb3ace44f30013af8a58a01e9a41a9aca497bd5591c060cabdcc542f25d5660b842548916282541fe869f627538ef51bb083c66
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
- * [#2083](https://github.com/ruby-grape/grape/pull/2083): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
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 explicitely passed - [@yaneq](https://github.com/yaneq).
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://travis-ci.org/ruby-grape/grape.svg?branch=master)](https://travis-ci.org/ruby-grape/grape)
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.1.
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
- run Twitter::API
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 raise an exception to indicate the value was invalid. E.g.,
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
- fail 'Invalid color' unless %w(blue red green).include?(value)
1185
- new(value)
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 descripcion could be done using: `desc configuration[:description]` if it may vary
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)
@@ -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 close around every
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 explicitely defined, auto-detect from relation#klass or first object in the collection
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
@@ -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 parameter names, or, if :using is
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
@@ -79,11 +79,12 @@ module Grape
79
79
  namespace_inheritable(:do_not_route_options, true)
80
80
  end
81
81
 
82
- def mount(mounts, **opts)
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
- mount(app.mount_instance(configuration: opts[:with] || {}) => path)
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, or group, resource, segment,
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
- # Thie method allows you to quickly define a parameter route segment
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
- # remove all the unnecessary stuff from Grape::Exceptions::Base like status
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
@@ -39,7 +39,7 @@ module Grape
39
39
  end
40
40
  end
41
41
 
42
- def to_json(**_opts)
42
+ def to_json(*_opts)
43
43
  as_json.to_json
44
44
  end
45
45
 
@@ -10,9 +10,9 @@ module Grape
10
10
 
11
11
  attr_accessor :options, :app, :env
12
12
 
13
- def initialize(app, **options)
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 = options[: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, **options)
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
 
@@ -27,7 +27,7 @@ module Grape
27
27
  }
28
28
  end
29
29
 
30
- def initialize(app, **options)
30
+ def initialize(app, *options)
31
31
  super
32
32
  self.class.send(:include, @options[:helpers]) if @options[:helpers]
33
33
  end
@@ -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, :opts, :block, :klass
9
+ attr_reader :args, :block, :klass
10
10
 
11
- def initialize(klass, *args, **opts, &block)
11
+ def initialize(klass, *args, &block)
12
12
  @klass = klass
13
- @args = 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
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
36
- def use_in(builder)
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, **kwargs, &block)
64
+ def insert(index, *args, &block)
74
65
  index = assert_index(index, :before)
75
- middleware = self.class::Middleware.new(*args, **kwargs, &block)
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, **kwargs, &block)
87
- middleware = self.class::Middleware.new(*args, **kwargs, &block)
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|
@@ -39,7 +39,7 @@ module Grape
39
39
  end
40
40
 
41
41
  def configuration
42
- @api.configuration.evaluate
42
+ @api.configuration.respond_to?(:evaluate) ? @api.configuration.evaluate : @api.configuration
43
43
  end
44
44
 
45
45
  # @return [Boolean] whether or not this entire scope needs to be
@@ -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
@@ -55,6 +55,8 @@ module Grape
55
55
  return if val.nil?
56
56
 
57
57
  coerced_val = @method.call(val)
58
+
59
+ return coerced_val if coerced_val.is_a?(InvalidValue)
58
60
  return InvalidValue.new unless coerced?(coerced_val)
59
61
  coerced_val
60
62
  end
@@ -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 [Hash] additional validation options
16
- def initialize(attrs, options, required, scope, **opts)
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
- @fail_fast = opts[:fail_fast] || false
22
- @allow_blank = opts[:allow_blank] || false
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(*_args)
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(params: [@scope.full_name(attr_name)], message: message(:coerce))
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Grape
4
4
  # The current version of Grape.
5
- VERSION = '1.5.1'
5
+ VERSION = '1.5.2'
6
6
  end
@@ -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
 
@@ -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 explicitely provided' do
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
@@ -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
- it 'is a custom type' do
231
- subject.params do
232
- requires :uri, coerce: SecureURIOnly
233
- end
234
- subject.get '/secure_uri' do
235
- params[:uri].class
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
- get 'secure_uri', uri: 'https://www.example.com'
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
- expect(last_response.status).to eq(200)
241
- expect(last_response.body).to eq('URI::HTTPS')
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
- get 'secure_uri', uri: 'http://www.example.com'
269
+ get 'whatever', name: 'Bob'
244
270
 
245
- expect(last_response.status).to eq(400)
246
- expect(last_response.body).to eq('uri is invalid')
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 Arry -> Array -> Hash -> String" do
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 Arry -> Hash -> String" do
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 = versioned_path(version_options.merge(version: version_name, path: 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.1
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: 2020-11-15 00:00:00.000000000 Z
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.1/CHANGELOG.md
373
- documentation_uri: https://www.rubydoc.info/gems/grape/1.5.1
374
- source_code_uri: https://github.com/ruby-grape/grape/tree/v1.5.1
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.0.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.