grape 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -2
  3. data/README.md +43 -10
  4. data/UPGRADING.md +91 -0
  5. data/grape.gemspec +5 -5
  6. data/lib/grape/api/instance.rb +13 -17
  7. data/lib/grape/api.rb +7 -14
  8. data/lib/grape/cookies.rb +2 -0
  9. data/lib/grape/dsl/callbacks.rb +1 -1
  10. data/lib/grape/dsl/desc.rb +3 -5
  11. data/lib/grape/dsl/helpers.rb +6 -4
  12. data/lib/grape/dsl/inside_route.rb +18 -9
  13. data/lib/grape/dsl/middleware.rb +4 -4
  14. data/lib/grape/dsl/parameters.rb +11 -7
  15. data/lib/grape/dsl/request_response.rb +9 -6
  16. data/lib/grape/dsl/routing.rb +7 -6
  17. data/lib/grape/dsl/settings.rb +5 -5
  18. data/lib/grape/endpoint.rb +21 -36
  19. data/lib/grape/error_formatter/json.rb +2 -6
  20. data/lib/grape/error_formatter/xml.rb +2 -6
  21. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  22. data/lib/grape/exceptions/validation.rb +2 -3
  23. data/lib/grape/exceptions/validation_errors.rb +1 -1
  24. data/lib/grape/formatter/json.rb +1 -0
  25. data/lib/grape/formatter/serializable_hash.rb +2 -1
  26. data/lib/grape/formatter/xml.rb +1 -0
  27. data/lib/grape/locale/en.yml +1 -1
  28. data/lib/grape/middleware/auth/base.rb +3 -3
  29. data/lib/grape/middleware/base.rb +4 -2
  30. data/lib/grape/middleware/error.rb +1 -1
  31. data/lib/grape/middleware/formatter.rb +4 -4
  32. data/lib/grape/middleware/stack.rb +10 -16
  33. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  34. data/lib/grape/middleware/versioner/header.rb +6 -4
  35. data/lib/grape/middleware/versioner/param.rb +1 -0
  36. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  37. data/lib/grape/middleware/versioner/path.rb +2 -0
  38. data/lib/grape/parser/json.rb +1 -1
  39. data/lib/grape/parser/xml.rb +1 -1
  40. data/lib/grape/path.rb +1 -0
  41. data/lib/grape/request.rb +3 -0
  42. data/lib/grape/router/attribute_translator.rb +1 -1
  43. data/lib/grape/router/pattern.rb +1 -1
  44. data/lib/grape/router/route.rb +2 -2
  45. data/lib/grape/router.rb +6 -0
  46. data/lib/grape/util/inheritable_setting.rb +1 -3
  47. data/lib/grape/util/lazy_value.rb +3 -2
  48. data/lib/grape/validations/attributes_iterator.rb +8 -0
  49. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  50. data/lib/grape/validations/params_scope.rb +92 -58
  51. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  52. data/lib/grape/validations/types/custom_type_coercer.rb +3 -2
  53. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  54. data/lib/grape/validations/types/invalid_value.rb +24 -0
  55. data/lib/grape/validations/types/json.rb +2 -1
  56. data/lib/grape/validations/types/primitive_coercer.rb +3 -3
  57. data/lib/grape/validations/types.rb +1 -4
  58. data/lib/grape/validations/validator_factory.rb +1 -1
  59. data/lib/grape/validations/validators/all_or_none.rb +1 -0
  60. data/lib/grape/validations/validators/as.rb +4 -8
  61. data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
  62. data/lib/grape/validations/validators/base.rb +12 -7
  63. data/lib/grape/validations/validators/coerce.rb +8 -9
  64. data/lib/grape/validations/validators/default.rb +1 -0
  65. data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
  66. data/lib/grape/validations/validators/multiple_params_base.rb +5 -2
  67. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
  68. data/lib/grape/validations/validators/presence.rb +1 -0
  69. data/lib/grape/validations/validators/regexp.rb +1 -0
  70. data/lib/grape/validations/validators/same_as.rb +1 -0
  71. data/lib/grape/validations/validators/values.rb +3 -0
  72. data/lib/grape/version.rb +1 -1
  73. data/lib/grape.rb +3 -1
  74. data/spec/grape/api/custom_validations_spec.rb +1 -0
  75. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  76. data/spec/grape/api_remount_spec.rb +9 -4
  77. data/spec/grape/api_spec.rb +203 -37
  78. data/spec/grape/dsl/callbacks_spec.rb +1 -1
  79. data/spec/grape/dsl/middleware_spec.rb +1 -1
  80. data/spec/grape/dsl/parameters_spec.rb +1 -0
  81. data/spec/grape/dsl/routing_spec.rb +1 -1
  82. data/spec/grape/endpoint/declared_spec.rb +259 -1
  83. data/spec/grape/endpoint_spec.rb +18 -5
  84. data/spec/grape/entity_spec.rb +10 -10
  85. data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
  86. data/spec/grape/middleware/error_spec.rb +1 -2
  87. data/spec/grape/middleware/formatter_spec.rb +2 -2
  88. data/spec/grape/middleware/stack_spec.rb +4 -3
  89. data/spec/grape/request_spec.rb +1 -1
  90. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
  91. data/spec/grape/validations/params_scope_spec.rb +37 -3
  92. data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
  93. data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
  94. data/spec/grape/validations/validators/coerce_spec.rb +129 -22
  95. data/spec/grape/validations/validators/except_values_spec.rb +2 -2
  96. data/spec/grape/validations/validators/values_spec.rb +15 -11
  97. data/spec/grape/validations_spec.rb +280 -0
  98. data/spec/shared/versioning_examples.rb +22 -22
  99. data/spec/spec_helper.rb +1 -1
  100. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  101. data/spec/support/versioned_helpers.rb +1 -1
  102. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f34ddc6f9f11346067b19a6d7f8e333e433a14c1866e68714815a9e8a457487a
4
- data.tar.gz: c95b6956bcb2e5f2e61035ec4af7ee3dcaaa53f7743bde69e7363c9a24edc317
3
+ metadata.gz: 7af0abe5fd1cb203ceda93732e74ded372552f7a859c6a3c6b847d1ac31c9c97
4
+ data.tar.gz: 103a38ad4d41eccbfef3240cd8b04fe17f2bc080a4265e4353b43076ce03fb15
5
5
  SHA512:
6
- metadata.gz: fef19eceff9814554a70cc0fa04d24e2cdbbba91af8f663a07a3278694c4cda2664b521d5390a57f8f1ccf7dc41dff85d602423d47aa27c87ca7d73b9d6e64ba
7
- data.tar.gz: f1b2f404754216ba613e0250a6788ce9bbd02bd0560f2981c1847ca917398ede0f429ae03cdaf31bd3447e20248310f4ba7837511fe811ceb4ddee3b7c59df23
6
+ metadata.gz: 8c8fb085cbaa5ea150e072a54e98546f4336966871225ae5587d70d115da62d420e1f67b2459f83c39c5d29717f311e93d09e0440dd4bf4a75d1f1cec7573022
7
+ data.tar.gz: c0f6eb027e3f43c309c59643ceb28c8f7d783466c0663f3b65e93be52a0c3148123110d9aa81dda842027ace7f0f319519b5cdc1fec7102d65811a84fbc2b2ed
data/CHANGELOG.md CHANGED
@@ -1,3 +1,52 @@
1
+ ### 1.6.0 (2021/10/04)
2
+
3
+ #### Features
4
+
5
+ * [#2190](https://github.com/ruby-grape/grape/pull/2190): Upgrade dev deps & drop Ruby 2.4.x support - [@dnesteryuk](https://github.com/dnesteryuk).
6
+
7
+ #### Fixes
8
+
9
+ * [#2176](https://github.com/ruby-grape/grape/pull/2176): Fix: OPTIONS fails if matching all routes - [@myxoh](https://github.com/myxoh).
10
+ * [#2177](https://github.com/ruby-grape/grape/pull/2177): Fix: `default` validator fails if preceded by `as` validator - [@Catsuko](https://github.com/Catsuko).
11
+ * [#2180](https://github.com/ruby-grape/grape/pull/2180): Call `super` in `API.inherited` - [@yogeshjain999](https://github.com/yogeshjain999).
12
+ * [#2189](https://github.com/ruby-grape/grape/pull/2189): Fix: rename parameters when using `:as` (behaviour and grape-swagger documentation) - [@Jack12816](https://github.com/Jack12816).
13
+
14
+ ### 1.5.3 (2021/03/07)
15
+
16
+ #### Fixes
17
+
18
+ * [#2161](https://github.com/ruby-grape/grape/pull/2157): Handle EOFError from Rack when given an empty multipart body - [@bschmeck](https://github.com/bschmeck).
19
+ * [#2162](https://github.com/ruby-grape/grape/pull/2162): Corrected a hash modification while iterating issue - [@Jack12816](https://github.com/Jack12816).
20
+ * [#2164](https://github.com/ruby-grape/grape/pull/2164): Fix: `coerce_with` is now called for params with `nil` value - [@braktar](https://github.com/braktar).
21
+
22
+ ### 1.5.2 (2021/02/06)
23
+
24
+ #### Features
25
+
26
+ * [#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).
27
+ * [#2145](https://github.com/ruby-grape/grape/pull/2145): Ruby 3.0 compatibility - [@ericproulx](https://github.com/ericproulx).
28
+ * [#2143](https://github.com/ruby-grape/grape/pull/2143): Enable GitHub Actions with updated RuboCop and Danger - [@anakinj](https://github.com/anakinj).
29
+
30
+ #### Fixes
31
+
32
+ * [#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).
33
+ * [#2137](https://github.com/ruby-grape/grape/pull/2137): Fix typos - [@johnny-miyake](https://github.com/johnny-miyake).
34
+ * [#2131](https://github.com/ruby-grape/grape/pull/2131): Fix Ruby 2.7 keyword deprecation warning in validators/coerce - [@K0H205](https://github.com/K0H205).
35
+ * [#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).
36
+ * [#2152](https://github.com/ruby-grape/grape/pull/2152): Fix configuration method inside namespaced params - [@fsainz](https://github.com/fsainz).
37
+
38
+ ### 1.5.1 (2020/11/15)
39
+
40
+ #### Fixes
41
+
42
+ * [#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).
43
+ * [#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).
44
+ * [#2127](https://github.com/ruby-grape/grape/pull/2127): Fix a performance issue with dependent params - [@dnesteryuk](https://github.com/dnesteryuk).
45
+ * [#2126](https://github.com/ruby-grape/grape/pull/2126): Fix warnings about redefined attribute accessors in `AttributeTranslator` - [@samsonjs](https://github.com/samsonjs).
46
+ * [#2121](https://github.com/ruby-grape/grape/pull/2121): Fix 2.7 deprecation warning in validator_factory - [@Legogris](https://github.com/Legogris).
47
+ * [#2115](https://github.com/ruby-grape/grape/pull/2115): Fix declared_params regression with multiple allowed types - [@stanhu](https://github.com/stanhu).
48
+ * [#2123](https://github.com/ruby-grape/grape/pull/2123): Fix 2.7 deprecation warning in middleware/stack - [@Legogris](https://github.com/Legogris).
49
+
1
50
  ### 1.5.0 (2020/10/05)
2
51
 
3
52
  #### Fixes
@@ -6,7 +55,7 @@
6
55
  * [#2103](https://github.com/ruby-grape/grape/pull/2103): Ensure complete declared params structure is present - [@tlconnor](https://github.com/tlconnor).
7
56
  * [#2099](https://github.com/ruby-grape/grape/pull/2099): Added truffleruby to Travis-CI - [@gogainda](https://github.com/gogainda).
8
57
  * [#2089](https://github.com/ruby-grape/grape/pull/2089): Specify order of mounting Grape with Rack::Cascade in README - [@jonmchan](https://github.com/jonmchan).
9
- * [#2083](https://github.com/ruby-grape/grape/pull/2083): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
58
+ * [#2088](https://github.com/ruby-grape/grape/pull/2088): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
10
59
  * [#2092](https://github.com/ruby-grape/grape/pull/2092): Correct an example params in Include Missing doc - [@huyvohcmc](https://github.com/huyvohcmc).
11
60
  * [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
12
61
  * [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
@@ -609,7 +658,7 @@
609
658
  * [#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).
610
659
  * [#495](https://github.com/ruby-grape/grape/pull/495): Fixed `ParamsScope#params` for parameters nested inside arrays - [@asross](https://github.com/asross).
611
660
  * [#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).
612
- * [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when explicitely passed - [@yaneq](https://github.com/yaneq).
661
+ * [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when explicitly passed - [@yaneq](https://github.com/yaneq).
613
662
  * [#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).
614
663
  * [#512](https://github.com/ruby-grape/grape/pull/512): Don't create `Grape::Request` multiple times - [@dblock](https://github.com/dblock).
615
664
  * [#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.0.
161
+ You're reading the documentation for the stable release of Grape, **1.6.0**.
160
162
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
161
163
 
162
164
  ## Project Resources
@@ -316,13 +318,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
316
318
  If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
317
319
  is handled correctly.
318
320
 
321
+ #### Rails 4
322
+
319
323
  The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
320
324
  `config.ru` before mounting Grape, e.g.:
321
325
 
322
326
  ```ruby
323
327
  use ActiveRecord::ConnectionAdapters::ConnectionManagement
328
+ ```
324
329
 
325
- run Twitter::API
330
+ #### Rails 5+
331
+
332
+ Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
333
+
334
+ ```ruby
335
+ use OTR::ActiveRecord::ConnectionManagement
326
336
  ```
327
337
 
328
338
  ### Alongside Sinatra (or other frameworks)
@@ -790,6 +800,7 @@ Grape allows you to access only the parameters that have been declared by your `
790
800
 
791
801
  * Filter out the params that have been passed, but are not allowed.
792
802
  * Include any optional params that are declared but not passed.
803
+ * Perform any parameter renaming on the resulting hash.
793
804
 
794
805
  Consider the following API endpoint:
795
806
 
@@ -984,8 +995,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
984
995
  ````json
985
996
  {
986
997
  "declared_params": {
987
- "first_name": "first name",
988
- "last_name": null
998
+ "user": {
999
+ "first_name": "first name",
1000
+ "last_name": null
1001
+ }
989
1002
  }
990
1003
  }
991
1004
  ````
@@ -1172,7 +1185,8 @@ Aside from the default set of supported types listed above, any class can be
1172
1185
  used as a type as long as an explicit coercion method is supplied. If the type
1173
1186
  implements a class-level `parse` method, Grape will use it automatically.
1174
1187
  This method must take one string argument and return an instance of the correct
1175
- type, or raise an exception to indicate the value was invalid. E.g.,
1188
+ type, or return an instance of `Grape::Types::InvalidValue` which optionally
1189
+ accepts a message to be returned in the response.
1176
1190
 
1177
1191
  ```ruby
1178
1192
  class Color
@@ -1182,8 +1196,9 @@ class Color
1182
1196
  end
1183
1197
 
1184
1198
  def self.parse(value)
1185
- fail 'Invalid color' unless %w(blue red green).include?(value)
1186
- new(value)
1199
+ return new(value) if %w[blue red green]).include?(value)
1200
+
1201
+ Grape::Types::InvalidValue.new('Unsupported color')
1187
1202
  end
1188
1203
  end
1189
1204
 
@@ -1215,6 +1230,7 @@ params do
1215
1230
  end
1216
1231
  end
1217
1232
  ```
1233
+ Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
1218
1234
 
1219
1235
  Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
1220
1236
  It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
@@ -1516,6 +1532,14 @@ end
1516
1532
 
1517
1533
  While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1518
1534
 
1535
+ Note that [allow_blank](#allow_blank) validator applies while using `:values`. In the following example the absence of `:allow_blank` does not prevent `:state` from receiving blank values because `:allow_blank` defaults to `true`.
1536
+
1537
+ ```ruby
1538
+ params do
1539
+ requires :state, type: Symbol, values: [:active, :inactive]
1540
+ end
1541
+ ```
1542
+
1519
1543
  #### `except_values`
1520
1544
 
1521
1545
  Parameters can be restricted from having a specific set of values with the `:except_values` option.
@@ -2039,10 +2063,10 @@ end
2039
2063
 
2040
2064
  # is NOT the same as
2041
2065
 
2042
- get ':status' do # this makes param[:status] available
2066
+ get ':status' do # this makes params[:status] available
2043
2067
  end
2044
2068
 
2045
- # This will make both param[:status_id] and param[:id] available
2069
+ # This will make both params[:status_id] and params[:id] available
2046
2070
 
2047
2071
  get 'statuses/:status_id/reviews/:id' do
2048
2072
  end
@@ -3628,6 +3652,14 @@ You can access the controller params, headers, and helpers through the context w
3628
3652
  Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
3629
3653
  You only have to implement the helpers to access the specific `env` variable.
3630
3654
 
3655
+ If you are using a custom application that is inherited from `Rails::Application` and need to insert a new middleware among the ones initiated via Rails, you will need to register it manually in your custom application class.
3656
+
3657
+ ```ruby
3658
+ class Company::Application < Rails::Application
3659
+ config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
3660
+ end
3661
+ ```
3662
+
3631
3663
  ### Remote IP
3632
3664
 
3633
3665
  By default you can access remote IP with `request.ip`. This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP [Rails-style](http://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails) with `ActionDispatch::RemoteIp`.
@@ -3932,6 +3964,7 @@ Grape integrates with following third-party tools:
3932
3964
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
3933
3965
  * **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
3934
3966
  * **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
3967
+ * **[Datadog APM](https://docs.datadoghq.com/tracing/)** - [ddtrace](https://github.com/datadog/dd-trace-rb) gem, [documentation](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#grape)
3935
3968
 
3936
3969
  ## Contributing to Grape
3937
3970
 
data/UPGRADING.md CHANGED
@@ -1,6 +1,95 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 1.6.0
5
+
6
+ #### Parameter renaming with :as
7
+
8
+ Prior to 1.6.0 the [parameter renaming](https://github.com/ruby-grape/grape#renaming) with `:as` was directly touching the request payload ([`#params`](https://github.com/ruby-grape/grape#parameters)) while duplicating the old and the new key to be both available in the hash. This allowed clients to bypass any validation in case they knew the internal name of the parameter. Unfortunately, in combination with [grape-swagger](https://github.com/ruby-grape/grape-swagger) the internal name (name set with `:as`) of the parameters were documented.
9
+
10
+ This behavior was fixed. Parameter renaming is now done when using the [`#declared(params)`](https://github.com/ruby-grape/grape#declared) parameters helper. This stops confusing validation/coercion behavior.
11
+
12
+ Here comes an illustration of the old and new behaviour as code:
13
+
14
+ ```ruby
15
+ # (1) Rename a to b, while client sends +a+
16
+ optional :a, type: Integer, as: :b
17
+ params = { a: 1 }
18
+ declared(params, include_missing: false)
19
+ # expected => { b: 1 }
20
+ # actual => { b: 1 }
21
+
22
+ # (2) Rename a to b, while client sends +b+
23
+ optional :a, type: Integer, as: :b, values: [1, 2, 3]
24
+ params = { b: '5' }
25
+ declared(params, include_missing: false)
26
+ # expected => { } (>= 1.6.0)
27
+ # actual => { b: '5' } (uncasted, unvalidated, <= 1.5.3)
28
+ ```
29
+
30
+ Another implication of this change is the dependent parameter resolution. Prior to 1.6.0 the following code produced an `Grape::Exceptions::UnknownParameter` because `:a` was replace by `:b`:
31
+
32
+ ```ruby
33
+ params do
34
+ optional :a, as: :b
35
+ given :a do # (<= 1.5.3 you had to reference +:b+ here to make it work)
36
+ requires :c
37
+ end
38
+ end
39
+ ```
40
+
41
+ This code now works without any errors, as the renaming is just an internal behaviour of the `#declared(params)` parameter helper.
42
+
43
+ See [#2189](https://github.com/ruby-grape/grape/pull/2189) for more information.
44
+
45
+ ### Upgrading to >= 1.5.3
46
+
47
+ #### Nil value and coercion
48
+
49
+ Prior to 1.2.5 version passing a `nil` value for a parameter with a custom coercer would invoke the coercer, and not passing a parameter would not invoke it.
50
+ This behavior was not tested or documented. Version 1.3.0 quietly changed this behavior, in such that `nil` values skipped the coercion. Version 1.5.3 fixes and documents this as follows:
51
+
52
+ ```ruby
53
+ class Api < Grape::API
54
+ params do
55
+ optional :value, type: Integer, coerce_with: ->(val) { val || 0 }
56
+ end
57
+
58
+ get 'example' do
59
+ params[:my_param]
60
+ end
61
+ get '/example', params: { value: nil }
62
+ # 1.5.2 = nil
63
+ # 1.5.3 = 0
64
+ get '/example', params: {}
65
+ # 1.5.2 = nil
66
+ # 1.5.3 = nil
67
+ end
68
+ ```
69
+ See [#2164](https://github.com/ruby-grape/grape/pull/2164) for more information.
70
+
71
+ ### Upgrading to >= 1.5.1
72
+
73
+ #### Dependent params
74
+
75
+ If you use [dependent params](https://github.com/ruby-grape/grape#dependent-parameters) with
76
+ `Grape::Extensions::Hash::ParamBuilder`, make sure a parameter to be dependent on is set as a Symbol.
77
+ If a String is given, a parameter that other parameters depend on won't be found even if it is present.
78
+
79
+ _Correct_:
80
+ ```ruby
81
+ given :matrix do
82
+ # dependent params
83
+ end
84
+ ```
85
+
86
+ _Wrong_:
87
+ ```ruby
88
+ given 'matrix' do
89
+ # dependent params
90
+ end
91
+ ```
92
+
4
93
  ### Upgrading to >= 1.5.0
5
94
 
6
95
  Prior to 1.3.3, the `declared` helper would always return the complete params structure if `include_missing=true` was set. In 1.3.3 a regression was introduced such that a missing Hash with or without nested parameters would always resolve to `{}`.
@@ -167,6 +256,8 @@ end
167
256
 
168
257
  ### Upgrading to >= 1.3.0
169
258
 
259
+ You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
260
+
170
261
  #### Ruby
171
262
 
172
263
  After adding dry-types, Ruby 2.4 or newer is required.
data/grape.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
4
4
  require 'grape/version'
5
5
 
6
6
  Gem::Specification.new do |s|
@@ -14,10 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.description = 'A Ruby framework for rapid API development with great conventions.'
15
15
  s.license = 'MIT'
16
16
  s.metadata = {
17
- 'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
18
- 'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
17
+ 'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
18
+ 'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
19
19
  'documentation_uri' => "https://www.rubydoc.info/gems/grape/#{s.version}",
20
- 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
20
+ 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
21
21
  }
22
22
 
23
23
  s.add_runtime_dependency 'activesupport'
@@ -32,5 +32,5 @@ Gem::Specification.new do |s|
32
32
  s.files += Dir['lib/**/*']
33
33
  s.test_files = Dir['spec/**/*']
34
34
  s.require_paths = ['lib']
35
- s.required_ruby_version = '>= 2.4.0'
35
+ s.required_ruby_version = '>= 2.5.0'
36
36
  end
@@ -10,12 +10,11 @@ module Grape
10
10
  include Grape::DSL::API
11
11
 
12
12
  class << self
13
- attr_reader :instance
14
- attr_reader :base
13
+ attr_reader :instance, :base
15
14
  attr_accessor :configuration
16
15
 
17
16
  def given(conditional_option, &block)
18
- evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block_given?
17
+ evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block
19
18
  end
20
19
 
21
20
  def mounted(&block)
@@ -28,7 +27,7 @@ module Grape
28
27
  end
29
28
 
30
29
  def to_s
31
- (base && base.to_s) || super
30
+ base&.to_s || super
32
31
  end
33
32
 
34
33
  def base_instance?
@@ -82,6 +81,7 @@ module Grape
82
81
 
83
82
  def compile!
84
83
  return if instance
84
+
85
85
  LOCK.synchronize { compile unless instance }
86
86
  end
87
87
 
@@ -103,7 +103,7 @@ module Grape
103
103
  def nest(*blocks, &block)
104
104
  blocks.reject!(&:nil?)
105
105
  if blocks.any?
106
- evaluate_as_instance_with_configuration(block) if block_given?
106
+ evaluate_as_instance_with_configuration(block) if block
107
107
  blocks.each { |b| evaluate_as_instance_with_configuration(b) }
108
108
  reset_validations!
109
109
  else
@@ -114,9 +114,7 @@ module Grape
114
114
  def evaluate_as_instance_with_configuration(block, lazy: false)
115
115
  lazy_block = Grape::Util::LazyBlock.new do |configuration|
116
116
  value_for_configuration = configuration
117
- if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
118
- self.configuration = value_for_configuration.evaluate
119
- end
117
+ self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
120
118
  response = instance_eval(&block)
121
119
  self.configuration = value_for_configuration
122
120
  response
@@ -179,7 +177,8 @@ module Grape
179
177
  # X-Cascade. Default :cascade is true.
180
178
  def cascade?
181
179
  return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.key?(:cascade)
182
- return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
180
+ return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options)&.key?(:cascade)
181
+
183
182
  true
184
183
  end
185
184
 
@@ -200,17 +199,15 @@ module Grape
200
199
  without_root_prefix do
201
200
  without_versioning do
202
201
  versioned_route_configs.each do |config|
202
+ next if config[:options][:matching_wildchar]
203
+
203
204
  allowed_methods = config[:methods].dup
204
205
 
205
- unless self.class.namespace_inheritable(:do_not_route_head)
206
- allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
207
- end
206
+ allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET)
208
207
 
209
208
  allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods)
210
209
 
211
- unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
212
- config[:endpoint].options[:options_route_enabled] = true
213
- end
210
+ config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
214
211
 
215
212
  attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
216
213
  generate_not_allowed_method(config[:pattern], **attributes)
@@ -228,7 +225,7 @@ module Grape
228
225
  last_route = routes.last # Most of the configuration is taken from the last endpoint
229
226
  matching_wildchar = routes.any? { |route| route.request_method == '*' }
230
227
  {
231
- options: {},
228
+ options: { matching_wildchar: matching_wildchar },
232
229
  pattern: last_route.pattern,
233
230
  requirements: last_route.requirements,
234
231
  path: last_route.origin,
@@ -248,7 +245,6 @@ module Grape
248
245
  Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
249
246
  end
250
247
  not_allowed_methods = supported_methods - allowed_methods
251
- return if not_allowed_methods.empty?
252
248
  @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
253
249
  end
254
250
 
data/lib/grape/api.rb CHANGED
@@ -20,10 +20,11 @@ module Grape
20
20
 
21
21
  # When inherited, will create a list of all instances (times the API was mounted)
22
22
  # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
23
- def inherited(api, base_instance_parent = Grape::API::Instance)
24
- api.initial_setup(base_instance_parent)
23
+ def inherited(api)
24
+ super
25
+
26
+ api.initial_setup(Grape::API == self ? Grape::API::Instance : @base_instance)
25
27
  api.override_all_methods!
26
- make_inheritable(api)
27
28
  end
28
29
 
29
30
  # Initialize the instance variables on the remountable class, and the base_instance
@@ -68,15 +69,6 @@ module Grape
68
69
  instance_for_rack.call(*args, &block)
69
70
  end
70
71
 
71
- # Allows an API to itself be inheritable:
72
- def make_inheritable(api)
73
- # When a child API inherits from a parent API.
74
- def api.inherited(child_api)
75
- # The instances of the child API inherit from the instances of the parent API
76
- Grape::API.inherited(child_api, base_instance)
77
- end
78
- end
79
-
80
72
  # Alleviates problems with autoloading by tring to search for the constant
81
73
  def const_missing(*args)
82
74
  if base_instance.const_defined?(*args)
@@ -87,7 +79,7 @@ module Grape
87
79
  end
88
80
 
89
81
  # 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
82
+ # For instance, a description could be done using: `desc configuration[:description]` if it may vary
91
83
  # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
92
84
  # too much, you may actually want to provide a new API rather than remount it.
93
85
  def mount_instance(**opts)
@@ -141,7 +133,7 @@ module Grape
141
133
  # Adds a new stage to the set up require to get a Grape::API up and running
142
134
  def add_setup(method, *args, &block)
143
135
  setup_step = { method: method, args: args, block: block }
144
- @setup << setup_step
136
+ @setup += [setup_step]
145
137
  last_response = nil
146
138
  @instances.each do |instance|
147
139
  last_response = replay_step_on(instance, setup_step)
@@ -151,6 +143,7 @@ module Grape
151
143
 
152
144
  def replay_step_on(instance, setup_step)
153
145
  return if skip_immediate_run?(instance, setup_step[:args])
146
+
154
147
  args = evaluate_arguments(instance.configuration, *setup_step[:args])
155
148
  response = instance.send(setup_step[:method], *args, &setup_step[:block])
156
149
  if skip_immediate_run?(instance, [response])
data/lib/grape/cookies.rb CHANGED
@@ -33,9 +33,11 @@ module Grape
33
33
  @cookies.each(&block)
34
34
  end
35
35
 
36
+ # rubocop:disable Layout/SpaceBeforeBrackets
36
37
  def delete(name, **opts)
37
38
  options = opts.merge(value: 'deleted', expires: Time.at(0))
38
39
  self.[]=(name, options)
39
40
  end
41
+ # rubocop:enable Layout/SpaceBeforeBrackets
40
42
  end
41
43
  end
@@ -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)
@@ -50,7 +50,7 @@ module Grape
50
50
  # end
51
51
  #
52
52
  def desc(description, options = {}, &config_block)
53
- if block_given?
53
+ if config_block
54
54
  endpoint_configuration = if defined?(configuration)
55
55
  # When the instance is mounted - the configuration is executed on mount time
56
56
  if configuration.respond_to?(:evaluate)
@@ -68,9 +68,7 @@ module Grape
68
68
  end
69
69
 
70
70
  config_class.configure(&config_block)
71
- unless options.empty?
72
- warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.'
73
- end
71
+ warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.' unless options.empty?
74
72
  options = config_class.settings
75
73
  else
76
74
  options = options.merge(description: description)
@@ -92,7 +90,7 @@ module Grape
92
90
 
93
91
  def unset_description_field(field)
94
92
  description = route_setting(:description)
95
- description.delete(field) if description
93
+ description&.delete(field)
96
94
  end
97
95
 
98
96
  # Returns an object which configures itself via an instance-context DSL.
@@ -36,8 +36,8 @@ module Grape
36
36
  #
37
37
  def helpers(*new_modules, &block)
38
38
  include_new_modules(new_modules) if new_modules.any?
39
- include_block(block) if block_given?
40
- include_all_in_scope if !block_given? && new_modules.empty?
39
+ include_block(block) if block
40
+ include_all_in_scope if !block && new_modules.empty?
41
41
  end
42
42
 
43
43
  protected
@@ -67,12 +67,13 @@ module Grape
67
67
 
68
68
  def define_boolean_in_mod(mod)
69
69
  return if defined? mod::Boolean
70
+
70
71
  mod.const_set('Boolean', Grape::API::Boolean)
71
72
  end
72
73
 
73
- def inject_api_helpers_to_mod(mod, &_block)
74
+ def inject_api_helpers_to_mod(mod, &block)
74
75
  mod.extend(BaseHelper) unless mod.is_a?(BaseHelper)
75
- yield if block_given?
76
+ yield if block
76
77
  mod.api_changed(self)
77
78
  end
78
79
  end
@@ -96,6 +97,7 @@ module Grape
96
97
 
97
98
  def process_named_params
98
99
  return unless instance_variable_defined?(:@named_params) && @named_params && @named_params.any?
100
+
99
101
  api.namespace_stackable(:named_params, @named_params)
100
102
  end
101
103
  end