grape 1.5.1 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -3
  3. data/CONTRIBUTING.md +1 -0
  4. data/README.md +50 -8
  5. data/UPGRADING.md +69 -0
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +13 -17
  8. data/lib/grape/api.rb +19 -14
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dry_types.rb +12 -0
  11. data/lib/grape/dsl/callbacks.rb +1 -1
  12. data/lib/grape/dsl/desc.rb +3 -5
  13. data/lib/grape/dsl/headers.rb +5 -2
  14. data/lib/grape/dsl/helpers.rb +7 -5
  15. data/lib/grape/dsl/inside_route.rb +18 -9
  16. data/lib/grape/dsl/middleware.rb +4 -4
  17. data/lib/grape/dsl/parameters.rb +4 -4
  18. data/lib/grape/dsl/request_response.rb +9 -6
  19. data/lib/grape/dsl/routing.rb +7 -6
  20. data/lib/grape/dsl/settings.rb +5 -5
  21. data/lib/grape/endpoint.rb +21 -36
  22. data/lib/grape/error_formatter/json.rb +2 -6
  23. data/lib/grape/error_formatter/xml.rb +2 -6
  24. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  25. data/lib/grape/exceptions/validation.rb +2 -3
  26. data/lib/grape/exceptions/validation_errors.rb +1 -1
  27. data/lib/grape/formatter/json.rb +1 -0
  28. data/lib/grape/formatter/serializable_hash.rb +2 -1
  29. data/lib/grape/formatter/xml.rb +1 -0
  30. data/lib/grape/locale/en.yml +1 -1
  31. data/lib/grape/middleware/auth/base.rb +3 -3
  32. data/lib/grape/middleware/auth/dsl.rb +7 -1
  33. data/lib/grape/middleware/base.rb +5 -3
  34. data/lib/grape/middleware/error.rb +1 -1
  35. data/lib/grape/middleware/formatter.rb +4 -4
  36. data/lib/grape/middleware/stack.rb +14 -20
  37. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  38. data/lib/grape/middleware/versioner/header.rb +6 -4
  39. data/lib/grape/middleware/versioner/param.rb +1 -0
  40. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  41. data/lib/grape/middleware/versioner/path.rb +2 -0
  42. data/lib/grape/parser/json.rb +1 -1
  43. data/lib/grape/parser/xml.rb +1 -1
  44. data/lib/grape/path.rb +1 -0
  45. data/lib/grape/request.rb +3 -0
  46. data/lib/grape/router/pattern.rb +1 -1
  47. data/lib/grape/router/route.rb +2 -2
  48. data/lib/grape/router.rb +6 -0
  49. data/lib/grape/util/inheritable_setting.rb +1 -3
  50. data/lib/grape/util/json.rb +2 -0
  51. data/lib/grape/util/lazy_value.rb +3 -2
  52. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  53. data/lib/grape/validations/params_scope.rb +89 -56
  54. data/lib/grape/validations/types/array_coercer.rb +0 -2
  55. data/lib/grape/validations/types/custom_type_coercer.rb +3 -2
  56. data/lib/grape/validations/types/dry_type_coercer.rb +2 -11
  57. data/lib/grape/validations/types/invalid_value.rb +24 -0
  58. data/lib/grape/validations/types/json.rb +2 -3
  59. data/lib/grape/validations/types/primitive_coercer.rb +5 -7
  60. data/lib/grape/validations/types/set_coercer.rb +0 -3
  61. data/lib/grape/validations/types.rb +83 -12
  62. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  63. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  64. data/lib/grape/validations/validators/as_validator.rb +14 -0
  65. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  66. data/lib/grape/validations/validators/base.rb +75 -69
  67. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  68. data/lib/grape/validations/validators/default_validator.rb +51 -0
  69. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  70. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  71. data/lib/grape/validations/validators/multiple_params_base.rb +24 -20
  72. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  73. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  74. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  75. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  76. data/lib/grape/validations/validators/values_validator.rb +88 -0
  77. data/lib/grape/version.rb +1 -1
  78. data/lib/grape.rb +61 -24
  79. data/spec/grape/api/custom_validations_spec.rb +77 -45
  80. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  81. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  82. data/spec/grape/api/invalid_format_spec.rb +2 -0
  83. data/spec/grape/api/recognize_path_spec.rb +1 -1
  84. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  85. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  86. data/spec/grape/api_remount_spec.rb +25 -19
  87. data/spec/grape/api_spec.rb +510 -220
  88. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  89. data/spec/grape/dsl/headers_spec.rb +39 -9
  90. data/spec/grape/dsl/helpers_spec.rb +3 -2
  91. data/spec/grape/dsl/inside_route_spec.rb +6 -4
  92. data/spec/grape/dsl/logger_spec.rb +16 -18
  93. data/spec/grape/dsl/middleware_spec.rb +2 -1
  94. data/spec/grape/dsl/parameters_spec.rb +2 -0
  95. data/spec/grape/dsl/request_response_spec.rb +1 -0
  96. data/spec/grape/dsl/routing_spec.rb +10 -7
  97. data/spec/grape/endpoint/declared_spec.rb +259 -12
  98. data/spec/grape/endpoint_spec.rb +77 -55
  99. data/spec/grape/entity_spec.rb +23 -23
  100. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  101. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  102. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  103. data/spec/grape/exceptions/validation_spec.rb +5 -3
  104. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  105. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  106. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  107. data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
  108. data/spec/grape/loading_spec.rb +8 -8
  109. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  110. data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
  111. data/spec/grape/middleware/base_spec.rb +24 -15
  112. data/spec/grape/middleware/error_spec.rb +2 -2
  113. data/spec/grape/middleware/exception_spec.rb +111 -161
  114. data/spec/grape/middleware/formatter_spec.rb +27 -6
  115. data/spec/grape/middleware/globals_spec.rb +7 -4
  116. data/spec/grape/middleware/stack_spec.rb +15 -14
  117. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  118. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  119. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  120. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  121. data/spec/grape/middleware/versioner_spec.rb +1 -1
  122. data/spec/grape/parser_spec.rb +4 -0
  123. data/spec/grape/path_spec.rb +52 -52
  124. data/spec/grape/presenters/presenter_spec.rb +7 -6
  125. data/spec/grape/request_spec.rb +6 -4
  126. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  127. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  128. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  129. data/spec/grape/util/stackable_values_spec.rb +7 -5
  130. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
  132. data/spec/grape/validations/params_scope_spec.rb +46 -10
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +2 -1
  134. data/spec/grape/validations/types/primitive_coercer_spec.rb +4 -4
  135. data/spec/grape/validations/types_spec.rb +8 -8
  136. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  137. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  138. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  139. data/spec/grape/validations/validators/coerce_spec.rb +139 -34
  140. data/spec/grape/validations/validators/default_spec.rb +72 -78
  141. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  142. data/spec/grape/validations/validators/except_values_spec.rb +3 -3
  143. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  144. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  145. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  146. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  147. data/spec/grape/validations/validators/values_spec.rb +183 -178
  148. data/spec/grape/validations_spec.rb +99 -58
  149. data/spec/integration/eager_load/eager_load_spec.rb +2 -2
  150. data/spec/integration/multi_json/json_spec.rb +1 -1
  151. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  152. data/spec/shared/versioning_examples.rb +32 -29
  153. data/spec/spec_helper.rb +12 -2
  154. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  155. data/spec/support/versioned_helpers.rb +1 -1
  156. metadata +117 -115
  157. data/lib/grape/validations/types/build_coercer.rb +0 -94
  158. data/lib/grape/validations/validators/all_or_none.rb +0 -15
  159. data/lib/grape/validations/validators/allow_blank.rb +0 -18
  160. data/lib/grape/validations/validators/as.rb +0 -16
  161. data/lib/grape/validations/validators/at_least_one_of.rb +0 -14
  162. data/lib/grape/validations/validators/coerce.rb +0 -88
  163. data/lib/grape/validations/validators/default.rb +0 -48
  164. data/lib/grape/validations/validators/exactly_one_of.rb +0 -16
  165. data/lib/grape/validations/validators/except_values.rb +0 -22
  166. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -15
  167. data/lib/grape/validations/validators/presence.rb +0 -12
  168. data/lib/grape/validations/validators/regexp.rb +0 -13
  169. data/lib/grape/validations/validators/same_as.rb +0 -26
  170. data/lib/grape/validations/validators/values.rb +0 -83
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc2b398e9ad95e7e4bb2641bbdaada929d992cd50fb551b56dbafe5f03a75589
4
- data.tar.gz: 03765fbe9d5dafb28b531f2a454f06b2ac59d16ea28691036de797b8e64ced06
3
+ metadata.gz: 1e8e622245d5871dfbaa651eac26abfd8bc4d36accd176f39fcc8ec09200cbe5
4
+ data.tar.gz: d92fe82ca78e6140bc98832671e682c2f090210e1ceaee907d9f3c4f812463b1
5
5
  SHA512:
6
- metadata.gz: a078c1c951ff6e5dfee34c78ab04e1f2866be316af23de469db12301ec3c86eb53bcdc9c8ac478aabe02bd3dc34f4772166e49bdf85ef731a051a8d0de73441f
7
- data.tar.gz: 7146c097cfced8308f1efb314fb3ace44f30013af8a58a01e9a41a9aca497bd5591c060cabdcc542f25d5660b842548916282541fe869f627538ef51bb083c66
6
+ metadata.gz: eed721c5ebd94fa07d2fa2a5420f0a943959890e85c71a941d4b1caede3605dbff836d4f42fabf0e15dec2a3bda944c7cd29988dcc1b1fd13eeea350c81a62de
7
+ data.tar.gz: ac951a0fbcd4d467ecf6f9b25f0d0d460a7cd132bb4ae708e2e0f55623ccc40d77809ca9c5663bde77c5d59708b264288a7592158340f1ca8336621007c8787b
data/CHANGELOG.md CHANGED
@@ -1,8 +1,62 @@
1
+ ### 1.6.1 (2021/12/28)
2
+
3
+ #### Features
4
+
5
+ * [#2196](https://github.com/ruby-grape/grape/pull/2196): Add support for `passwords_hashed` param for `digest_auth` - [@lHydra](https://github.com/lhydra).
6
+ * [#2208](https://github.com/ruby-grape/grape/pull/2208): Added Rails 7 support - [@ericproulx](https://github.com/ericproulx).
7
+
8
+ #### Fixes
9
+
10
+ * [#2206](https://github.com/ruby-grape/grape/pull/2206): Require main active_support lib before any of its extension definitions - [@annih](https://github.com/Annih).
11
+ * [#2193](https://github.com/ruby-grape/grape/pull/2193): Fixed the broken ruby-head NoMethodError spec - [@Jack12816](https://github.com/Jack12816).
12
+ * [#2192](https://github.com/ruby-grape/grape/pull/2192): Memoize the result of Grape::Middleware::Base#response - [@Jack12816](https://github.com/Jack12816).
13
+ * [#2200](https://github.com/ruby-grape/grape/pull/2200): Add validators module to all validators - [@ericproulx](https://github.com/ericproulx).
14
+ * [#2202](https://github.com/ruby-grape/grape/pull/2202): Fix random mock spec error - [@ericproulx](https://github.com/ericproulx).
15
+ * [#2203](https://github.com/ruby-grape/grape/pull/2203): Add rubocop-rspec - [@ericproulx](https://github.com/ericproulx).
16
+ * [#2207](https://github.com/ruby-grape/grape/pull/2207): Autoload Validations/Validators - [@ericproulx](https://github.com/ericproulx).
17
+ * [#2209](https://github.com/ruby-grape/grape/pull/2209): Autoload Validations/Types - [@ericproulx](https://github.com/ericproulx).
18
+
19
+ ### 1.6.0 (2021/10/04)
20
+
21
+ #### Features
22
+
23
+ * [#2190](https://github.com/ruby-grape/grape/pull/2190): Upgrade dev deps & drop Ruby 2.4.x support - [@dnesteryuk](https://github.com/dnesteryuk).
24
+
25
+ #### Fixes
26
+
27
+ * [#2176](https://github.com/ruby-grape/grape/pull/2176): Fix: OPTIONS fails if matching all routes - [@myxoh](https://github.com/myxoh).
28
+ * [#2177](https://github.com/ruby-grape/grape/pull/2177): Fix: `default` validator fails if preceded by `as` validator - [@Catsuko](https://github.com/Catsuko).
29
+ * [#2180](https://github.com/ruby-grape/grape/pull/2180): Call `super` in `API.inherited` - [@yogeshjain999](https://github.com/yogeshjain999).
30
+ * [#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).
31
+
32
+ ### 1.5.3 (2021/03/07)
33
+
34
+ #### Fixes
35
+
36
+ * [#2161](https://github.com/ruby-grape/grape/pull/2157): Handle EOFError from Rack when given an empty multipart body - [@bschmeck](https://github.com/bschmeck).
37
+ * [#2162](https://github.com/ruby-grape/grape/pull/2162): Corrected a hash modification while iterating issue - [@Jack12816](https://github.com/Jack12816).
38
+ * [#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).
39
+
40
+ ### 1.5.2 (2021/02/06)
41
+
42
+ #### Features
43
+
44
+ * [#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).
45
+ * [#2145](https://github.com/ruby-grape/grape/pull/2145): Ruby 3.0 compatibility - [@ericproulx](https://github.com/ericproulx).
46
+ * [#2143](https://github.com/ruby-grape/grape/pull/2143): Enable GitHub Actions with updated RuboCop and Danger - [@anakinj](https://github.com/anakinj).
47
+
48
+ #### Fixes
49
+
50
+ * [#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).
51
+ * [#2137](https://github.com/ruby-grape/grape/pull/2137): Fix typos - [@johnny-miyake](https://github.com/johnny-miyake).
52
+ * [#2131](https://github.com/ruby-grape/grape/pull/2131): Fix Ruby 2.7 keyword deprecation warning in validators/coerce - [@K0H205](https://github.com/K0H205).
53
+ * [#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).
54
+ * [#2152](https://github.com/ruby-grape/grape/pull/2152): Fix configuration method inside namespaced params - [@fsainz](https://github.com/fsainz).
55
+
1
56
  ### 1.5.1 (2020/11/15)
2
57
 
3
58
  #### Fixes
4
59
 
5
- * Your contribution here.
6
60
  * [#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
61
  * [#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
62
  * [#2127](https://github.com/ruby-grape/grape/pull/2127): Fix a performance issue with dependent params - [@dnesteryuk](https://github.com/dnesteryuk).
@@ -19,7 +73,7 @@
19
73
  * [#2103](https://github.com/ruby-grape/grape/pull/2103): Ensure complete declared params structure is present - [@tlconnor](https://github.com/tlconnor).
20
74
  * [#2099](https://github.com/ruby-grape/grape/pull/2099): Added truffleruby to Travis-CI - [@gogainda](https://github.com/gogainda).
21
75
  * [#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).
76
+ * [#2088](https://github.com/ruby-grape/grape/pull/2088): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
23
77
  * [#2092](https://github.com/ruby-grape/grape/pull/2092): Correct an example params in Include Missing doc - [@huyvohcmc](https://github.com/huyvohcmc).
24
78
  * [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
25
79
  * [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
@@ -622,7 +676,7 @@
622
676
  * [#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
677
  * [#495](https://github.com/ruby-grape/grape/pull/495): Fixed `ParamsScope#params` for parameters nested inside arrays - [@asross](https://github.com/asross).
624
678
  * [#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).
679
+ * [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when explicitly passed - [@yaneq](https://github.com/yaneq).
626
680
  * [#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
681
  * [#512](https://github.com/ruby-grape/grape/pull/512): Don't create `Grape::Request` multiple times - [@dblock](https://github.com/dblock).
628
682
  * [#538](https://github.com/ruby-grape/grape/pull/538): Fixed default values for grouped params - [@dm1try](https://github.com/dm1try).
data/CONTRIBUTING.md CHANGED
@@ -35,6 +35,7 @@ bundle exec rake
35
35
  Run tests against all supported versions of Rails.
36
36
 
37
37
  ```
38
+ gem install appraisal
38
39
  appraisal install
39
40
  appraisal rake spec
40
41
  ```
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,8 @@ 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.6.1**.
162
+ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
160
163
 
161
164
  ## Project Resources
162
165
 
@@ -315,13 +318,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
315
318
  If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
316
319
  is handled correctly.
317
320
 
321
+ #### Rails 4
322
+
318
323
  The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
319
324
  `config.ru` before mounting Grape, e.g.:
320
325
 
321
326
  ```ruby
322
327
  use ActiveRecord::ConnectionAdapters::ConnectionManagement
328
+ ```
323
329
 
324
- 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
325
336
  ```
326
337
 
327
338
  ### Alongside Sinatra (or other frameworks)
@@ -789,6 +800,7 @@ Grape allows you to access only the parameters that have been declared by your `
789
800
 
790
801
  * Filter out the params that have been passed, but are not allowed.
791
802
  * Include any optional params that are declared but not passed.
803
+ * Perform any parameter renaming on the resulting hash.
792
804
 
793
805
  Consider the following API endpoint:
794
806
 
@@ -983,8 +995,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
983
995
  ````json
984
996
  {
985
997
  "declared_params": {
986
- "first_name": "first name",
987
- "last_name": null
998
+ "user": {
999
+ "first_name": "first name",
1000
+ "last_name": null
1001
+ }
988
1002
  }
989
1003
  }
990
1004
  ````
@@ -1171,7 +1185,8 @@ Aside from the default set of supported types listed above, any class can be
1171
1185
  used as a type as long as an explicit coercion method is supplied. If the type
1172
1186
  implements a class-level `parse` method, Grape will use it automatically.
1173
1187
  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.,
1188
+ type, or return an instance of `Grape::Types::InvalidValue` which optionally
1189
+ accepts a message to be returned in the response.
1175
1190
 
1176
1191
  ```ruby
1177
1192
  class Color
@@ -1181,8 +1196,9 @@ class Color
1181
1196
  end
1182
1197
 
1183
1198
  def self.parse(value)
1184
- fail 'Invalid color' unless %w(blue red green).include?(value)
1185
- new(value)
1199
+ return new(value) if %w[blue red green]).include?(value)
1200
+
1201
+ Grape::Types::InvalidValue.new('Unsupported color')
1186
1202
  end
1187
1203
  end
1188
1204
 
@@ -1214,6 +1230,7 @@ params do
1214
1230
  end
1215
1231
  end
1216
1232
  ```
1233
+ Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
1217
1234
 
1218
1235
  Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
1219
1236
  It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
@@ -1515,6 +1532,14 @@ end
1515
1532
 
1516
1533
  While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1517
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
+
1518
1543
  #### `except_values`
1519
1544
 
1520
1545
  Parameters can be restricted from having a specific set of values with the `:except_values` option.
@@ -3271,12 +3296,20 @@ http_basic do |username, password|
3271
3296
  end
3272
3297
  ```
3273
3298
 
3299
+ Digest auth supports clear-text passwords and password hashes.
3300
+
3274
3301
  ```ruby
3275
3302
  http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
3276
3303
  # lookup the user's password here
3277
3304
  end
3278
3305
  ```
3279
3306
 
3307
+ ```ruby
3308
+ http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
3309
+ # lookup the user's password hash here
3310
+ end
3311
+ ```
3312
+
3280
3313
  ### Register custom middleware for authentication
3281
3314
 
3282
3315
  Grape can use custom Middleware for authentication. How to implement these
@@ -3627,6 +3660,14 @@ You can access the controller params, headers, and helpers through the context w
3627
3660
  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.
3628
3661
  You only have to implement the helpers to access the specific `env` variable.
3629
3662
 
3663
+ 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.
3664
+
3665
+ ```ruby
3666
+ class Company::Application < Rails::Application
3667
+ config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
3668
+ end
3669
+ ```
3670
+
3630
3671
  ### Remote IP
3631
3672
 
3632
3673
  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`.
@@ -3931,6 +3972,7 @@ Grape integrates with following third-party tools:
3931
3972
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
3932
3973
  * **[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)
3933
3974
  * **[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)
3975
+ * **[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)
3934
3976
 
3935
3977
  ## Contributing to Grape
3936
3978
 
data/UPGRADING.md CHANGED
@@ -1,6 +1,73 @@
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
+
4
71
  ### Upgrading to >= 1.5.1
5
72
 
6
73
  #### Dependent params
@@ -189,6 +256,8 @@ end
189
256
 
190
257
  ### Upgrading to >= 1.3.0
191
258
 
259
+ You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
260
+
192
261
  #### Ruby
193
262
 
194
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
@@ -10,6 +10,18 @@ module Grape
10
10
  # Class methods that we want to call on the API rather than on the API object
11
11
  NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile! inherited]).freeze
12
12
 
13
+ class Boolean
14
+ def self.build(val)
15
+ return nil if val != true && val != false
16
+
17
+ new
18
+ end
19
+ end
20
+
21
+ class Instance
22
+ Boolean = Grape::API::Boolean
23
+ end
24
+
13
25
  class << self
14
26
  attr_accessor :base_instance, :instances
15
27
 
@@ -20,10 +32,11 @@ module Grape
20
32
 
21
33
  # When inherited, will create a list of all instances (times the API was mounted)
22
34
  # 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)
35
+ def inherited(api)
36
+ super
37
+
38
+ api.initial_setup(Grape::API == self ? Grape::API::Instance : @base_instance)
25
39
  api.override_all_methods!
26
- make_inheritable(api)
27
40
  end
28
41
 
29
42
  # Initialize the instance variables on the remountable class, and the base_instance
@@ -68,15 +81,6 @@ module Grape
68
81
  instance_for_rack.call(*args, &block)
69
82
  end
70
83
 
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
84
  # Alleviates problems with autoloading by tring to search for the constant
81
85
  def const_missing(*args)
82
86
  if base_instance.const_defined?(*args)
@@ -87,7 +91,7 @@ module Grape
87
91
  end
88
92
 
89
93
  # 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
94
+ # For instance, a description could be done using: `desc configuration[:description]` if it may vary
91
95
  # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
92
96
  # too much, you may actually want to provide a new API rather than remount it.
93
97
  def mount_instance(**opts)
@@ -141,7 +145,7 @@ module Grape
141
145
  # Adds a new stage to the set up require to get a Grape::API up and running
142
146
  def add_setup(method, *args, &block)
143
147
  setup_step = { method: method, args: args, block: block }
144
- @setup << setup_step
148
+ @setup += [setup_step]
145
149
  last_response = nil
146
150
  @instances.each do |instance|
147
151
  last_response = replay_step_on(instance, setup_step)
@@ -151,6 +155,7 @@ module Grape
151
155
 
152
156
  def replay_step_on(instance, setup_step)
153
157
  return if skip_immediate_run?(instance, setup_step[:args])
158
+
154
159
  args = evaluate_arguments(instance.configuration, *setup_step[:args])
155
160
  response = instance.send(setup_step[:method], *args, &setup_step[:block])
156
161
  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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+
5
+ module Grape
6
+ module DryTypes
7
+ # Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
8
+ # a container in this case. Check documentation for more information
9
+ # https://dry-rb.org/gems/dry-types/1.2/getting-started/
10
+ include Dry.Types()
11
+ end
12
+ 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.
@@ -3,8 +3,11 @@
3
3
  module Grape
4
4
  module DSL
5
5
  module Headers
6
- # Set an individual header or retrieve
7
- # all headers that have been set.
6
+ # This method has four responsibilities:
7
+ # 1. Set a specifc header value by key
8
+ # 2. Retrieve a specifc header value by key
9
+ # 3. Retrieve all headers that have been set
10
+ # 4. Delete a specifc header key-value pair
8
11
  def header(key = nil, val = nil)
9
12
  if key
10
13
  val ? header[key.to_s] = val : header.delete(key.to_s)