grape 1.8.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (210) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -1
  3. data/README.md +377 -334
  4. data/UPGRADING.md +231 -6
  5. data/grape.gemspec +6 -10
  6. data/lib/grape/api/instance.rb +13 -10
  7. data/lib/grape/api.rb +17 -8
  8. data/lib/grape/content_types.rb +0 -2
  9. data/lib/grape/cookies.rb +2 -1
  10. data/lib/grape/dry_types.rb +0 -2
  11. data/lib/grape/dsl/desc.rb +23 -21
  12. data/lib/grape/dsl/headers.rb +1 -1
  13. data/lib/grape/dsl/inside_route.rb +47 -22
  14. data/lib/grape/dsl/parameters.rb +4 -3
  15. data/lib/grape/dsl/routing.rb +20 -4
  16. data/lib/grape/dsl/validations.rb +13 -0
  17. data/lib/grape/endpoint.rb +15 -10
  18. data/lib/grape/{util/env.rb → env.rb} +0 -5
  19. data/lib/grape/error_formatter/txt.rb +11 -10
  20. data/lib/grape/exceptions/base.rb +3 -3
  21. data/lib/grape/exceptions/missing_group_type.rb +1 -1
  22. data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
  23. data/lib/grape/exceptions/validation.rb +0 -2
  24. data/lib/grape/exceptions/validation_array_errors.rb +1 -0
  25. data/lib/grape/exceptions/validation_errors.rb +1 -3
  26. data/lib/grape/extensions/hash.rb +5 -1
  27. data/lib/grape/http/headers.rb +18 -24
  28. data/lib/grape/{util/json.rb → json.rb} +1 -3
  29. data/lib/grape/locale/en.yml +3 -0
  30. data/lib/grape/middleware/auth/base.rb +0 -2
  31. data/lib/grape/middleware/auth/dsl.rb +0 -2
  32. data/lib/grape/middleware/auth/strategies.rb +1 -2
  33. data/lib/grape/middleware/base.rb +0 -2
  34. data/lib/grape/middleware/error.rb +55 -50
  35. data/lib/grape/middleware/formatter.rb +21 -18
  36. data/lib/grape/middleware/globals.rb +1 -3
  37. data/lib/grape/middleware/stack.rb +2 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  39. data/lib/grape/middleware/versioner/header.rb +17 -163
  40. data/lib/grape/middleware/versioner/param.rb +2 -4
  41. data/lib/grape/middleware/versioner/path.rb +1 -3
  42. data/lib/grape/namespace.rb +3 -4
  43. data/lib/grape/path.rb +24 -29
  44. data/lib/grape/railtie.rb +9 -0
  45. data/lib/grape/request.rb +3 -5
  46. data/lib/grape/router/base_route.rb +39 -0
  47. data/lib/grape/router/greedy_route.rb +20 -0
  48. data/lib/grape/router/pattern.rb +39 -30
  49. data/lib/grape/router/route.rb +22 -59
  50. data/lib/grape/router.rb +30 -36
  51. data/lib/grape/util/accept/header.rb +19 -0
  52. data/lib/grape/util/accept_header_handler.rb +105 -0
  53. data/lib/grape/util/base_inheritable.rb +4 -4
  54. data/lib/grape/util/cache.rb +0 -3
  55. data/lib/grape/util/endpoint_configuration.rb +1 -1
  56. data/lib/grape/util/header.rb +13 -0
  57. data/lib/grape/util/inheritable_values.rb +0 -2
  58. data/lib/grape/util/lazy/block.rb +29 -0
  59. data/lib/grape/util/lazy/object.rb +45 -0
  60. data/lib/grape/util/lazy/value.rb +38 -0
  61. data/lib/grape/util/lazy/value_array.rb +21 -0
  62. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  63. data/lib/grape/util/lazy/value_hash.rb +21 -0
  64. data/lib/grape/util/media_type.rb +70 -0
  65. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  66. data/lib/grape/util/stackable_values.rb +1 -6
  67. data/lib/grape/util/strict_hash_configuration.rb +3 -3
  68. data/lib/grape/validations/attributes_doc.rb +38 -36
  69. data/lib/grape/validations/contract_scope.rb +71 -0
  70. data/lib/grape/validations/params_scope.rb +10 -9
  71. data/lib/grape/validations/types/array_coercer.rb +0 -2
  72. data/lib/grape/validations/types/build_coercer.rb +69 -71
  73. data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
  74. data/lib/grape/validations/types/json.rb +0 -2
  75. data/lib/grape/validations/types/primitive_coercer.rb +0 -2
  76. data/lib/grape/validations/types/set_coercer.rb +0 -3
  77. data/lib/grape/validations/types.rb +0 -3
  78. data/lib/grape/validations/validators/base.rb +2 -1
  79. data/lib/grape/validations/validators/default_validator.rb +5 -1
  80. data/lib/grape/validations/validators/length_validator.rb +42 -0
  81. data/lib/grape/validations/validators/values_validator.rb +8 -3
  82. data/lib/grape/validations.rb +3 -7
  83. data/lib/grape/version.rb +1 -1
  84. data/lib/grape/{util/xml.rb → xml.rb} +1 -1
  85. data/lib/grape.rb +38 -269
  86. metadata +33 -274
  87. data/lib/grape/eager_load.rb +0 -20
  88. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  89. data/lib/grape/router/attribute_translator.rb +0 -63
  90. data/lib/grape/util/lazy_block.rb +0 -27
  91. data/lib/grape/util/lazy_object.rb +0 -43
  92. data/lib/grape/util/lazy_value.rb +0 -91
  93. data/spec/grape/api/custom_validations_spec.rb +0 -213
  94. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  95. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  96. data/spec/grape/api/documentation_spec.rb +0 -59
  97. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  98. data/spec/grape/api/instance_spec.rb +0 -103
  99. data/spec/grape/api/invalid_format_spec.rb +0 -45
  100. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  101. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  102. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  103. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  104. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  105. data/spec/grape/api/recognize_path_spec.rb +0 -21
  106. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  107. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  108. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  109. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  110. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  111. data/spec/grape/api_remount_spec.rb +0 -509
  112. data/spec/grape/api_spec.rb +0 -4356
  113. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  114. data/spec/grape/dsl/desc_spec.rb +0 -98
  115. data/spec/grape/dsl/headers_spec.rb +0 -62
  116. data/spec/grape/dsl/helpers_spec.rb +0 -100
  117. data/spec/grape/dsl/inside_route_spec.rb +0 -531
  118. data/spec/grape/dsl/logger_spec.rb +0 -24
  119. data/spec/grape/dsl/middleware_spec.rb +0 -60
  120. data/spec/grape/dsl/parameters_spec.rb +0 -180
  121. data/spec/grape/dsl/request_response_spec.rb +0 -225
  122. data/spec/grape/dsl/routing_spec.rb +0 -275
  123. data/spec/grape/dsl/settings_spec.rb +0 -261
  124. data/spec/grape/dsl/validations_spec.rb +0 -55
  125. data/spec/grape/endpoint/declared_spec.rb +0 -846
  126. data/spec/grape/endpoint_spec.rb +0 -1085
  127. data/spec/grape/entity_spec.rb +0 -336
  128. data/spec/grape/exceptions/base_spec.rb +0 -81
  129. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
  130. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  131. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  132. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  133. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  134. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
  135. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  136. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  137. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  138. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  139. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
  140. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  141. data/spec/grape/exceptions/validation_spec.rb +0 -19
  142. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  143. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  144. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  145. data/spec/grape/grape_spec.rb +0 -9
  146. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  147. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  148. data/spec/grape/integration/rack_spec.rb +0 -51
  149. data/spec/grape/loading_spec.rb +0 -44
  150. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  151. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  152. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  153. data/spec/grape/middleware/base_spec.rb +0 -221
  154. data/spec/grape/middleware/error_spec.rb +0 -85
  155. data/spec/grape/middleware/exception_spec.rb +0 -294
  156. data/spec/grape/middleware/formatter_spec.rb +0 -461
  157. data/spec/grape/middleware/globals_spec.rb +0 -30
  158. data/spec/grape/middleware/stack_spec.rb +0 -155
  159. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  160. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  161. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  162. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  163. data/spec/grape/middleware/versioner_spec.rb +0 -21
  164. data/spec/grape/named_api_spec.rb +0 -19
  165. data/spec/grape/parser_spec.rb +0 -86
  166. data/spec/grape/path_spec.rb +0 -252
  167. data/spec/grape/presenters/presenter_spec.rb +0 -71
  168. data/spec/grape/request_spec.rb +0 -126
  169. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  170. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  171. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  172. data/spec/grape/util/stackable_values_spec.rb +0 -128
  173. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  174. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  175. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  176. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
  177. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  178. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
  179. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  180. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  181. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  182. data/spec/grape/validations/types_spec.rb +0 -111
  183. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  184. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  185. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  186. data/spec/grape/validations/validators/base_spec.rb +0 -38
  187. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  188. data/spec/grape/validations/validators/default_spec.rb +0 -463
  189. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  190. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  191. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  192. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  193. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  194. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  195. data/spec/grape/validations/validators/values_spec.rb +0 -733
  196. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  197. data/spec/grape/validations_spec.rb +0 -2030
  198. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  199. data/spec/integration/multi_json/json_spec.rb +0 -7
  200. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  201. data/spec/shared/deprecated_class_examples.rb +0 -16
  202. data/spec/shared/versioning_examples.rb +0 -215
  203. data/spec/spec_helper.rb +0 -52
  204. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  205. data/spec/support/chunks.rb +0 -14
  206. data/spec/support/content_type_helpers.rb +0 -15
  207. data/spec/support/endpoint_faker.rb +0 -25
  208. data/spec/support/file_streamer.rb +0 -13
  209. data/spec/support/integer_helpers.rb +0 -13
  210. data/spec/support/versioned_helpers.rb +0 -55
data/UPGRADING.md CHANGED
@@ -1,6 +1,234 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 2.1.0
5
+
6
+ #### Optional Builder
7
+
8
+ The `builder` gem dependency has been made optional as it's only used when generating XML. If your code does, add `builder` to your `Gemfile`.
9
+
10
+ See [#2445](https://github.com/ruby-grape/grape/pull/2445) for more information.
11
+
12
+ #### Deep Merging of Parameter Attributes
13
+
14
+ Grape now uses `deep_merge` to combine parameter attributes within the `with` method. Previously, attributes defined at the parameter level would override those defined at the group level.
15
+ With deep merge, attributes are now combined, allowing for more detailed and nuanced API specifications.
16
+
17
+ For example:
18
+
19
+ ```ruby
20
+ with(documentation: { in: 'body' }) do
21
+ optional :vault, documentation: { default: 33 }
22
+ end
23
+ ```
24
+
25
+ Before it was equivalent to:
26
+
27
+ ```ruby
28
+ optional :vault, documentation: { default: 33 }
29
+ ```
30
+
31
+ After it is an equivalent of:
32
+
33
+ ```ruby
34
+ optional :vault, documentation: { in: 'body', default: 33 }
35
+ ```
36
+
37
+ See [#2432](https://github.com/ruby-grape/grape/pull/2432) for more information.
38
+
39
+ #### Zeitwerk
40
+
41
+ Grape's autoloader has been updated and it's now based on [Zeitwerk](https://github.com/fxn/zeitwerk).
42
+ If you MP (Monkey Patch) some files and you're not following the [file structure](https://github.com/fxn/zeitwerk?tab=readme-ov-file#file-structure), you might end up with a Zeitwerk error.
43
+
44
+ See [#2363](https://github.com/ruby-grape/grape/pull/2363) for more information.
45
+
46
+ #### Changes in rescue_from
47
+
48
+ The `rack_response` method has been deprecated and the `error_response` method has been removed. Use `error!` instead.
49
+
50
+ See [#2414](https://github.com/ruby-grape/grape/pull/2414) for more information.
51
+
52
+ #### Change in parameters precedence
53
+
54
+ When using together with `Grape::Extensions::Hash::ParamBuilder`, `route_param` takes higher precedence over a regular parameter defined with same name, which now matches the default param builder behavior.
55
+
56
+ This was a regression introduced by [#2326](https://github.com/ruby-grape/grape/pull/2326) in Grape v1.8.0.
57
+
58
+ ```ruby
59
+ grape.configure do |config|
60
+ config.param_builder = Grape::Extensions::Hash::ParamBuilder
61
+ end
62
+
63
+ params do
64
+ requires :foo, type: String
65
+ end
66
+ route_param :foo do
67
+ get do
68
+ { value: params[:foo] }
69
+ end
70
+ end
71
+ ```
72
+
73
+ Request:
74
+
75
+ ```bash
76
+ curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
77
+ ```
78
+
79
+ Response prior to v1.8.0:
80
+
81
+ ```json
82
+ {
83
+ "value": "bar"
84
+ }
85
+ ```
86
+
87
+ v1.8.0..v2.0.0:
88
+
89
+ ```json
90
+ {
91
+ "value": "baz"
92
+ }
93
+ ```
94
+
95
+ v2.1.0+:
96
+
97
+ ```json
98
+ {
99
+ "value": "bar"
100
+ }
101
+ ```
102
+
103
+ See [#2378](https://github.com/ruby-grape/grape/pull/2378) for details.
104
+
105
+ #### Grape::Router::Route.route_xxx methods have been removed
106
+
107
+ - `route_method` is accessible through `request_method`
108
+ - `route_path` is accessible through `path`
109
+ - Any other `route_xyz` are accessible through `options[xyz]`
110
+
111
+ #### Instance variables scope
112
+
113
+ Due to the changes done in [#2377](https://github.com/ruby-grape/grape/pull/2377), the instance variables defined inside each of the endpoints (or inside a `before` validator) are now accessible inside the `rescue_from`. The behavior of the instance variables was undefined until `2.1.0`.
114
+
115
+ If you were using the same variable name defined inside an endpoint or `before` validator inside a `rescue_from` handler, you need to take in mind that you can start getting different values or you can be overriding values.
116
+
117
+ Before:
118
+ ```ruby
119
+ class TwitterAPI < Grape::API
120
+ before do
121
+ @var = 1
122
+ end
123
+
124
+ get '/' do
125
+ puts @var # => 1
126
+ raise
127
+ end
128
+
129
+ rescue_from :all do
130
+ puts @var # => nil
131
+ end
132
+ end
133
+ ```
134
+
135
+ After:
136
+ ```ruby
137
+ class TwitterAPI < Grape::API
138
+ before do
139
+ @var = 1
140
+ end
141
+
142
+ get '/' do
143
+ puts @var # => 1
144
+ raise
145
+ end
146
+
147
+ rescue_from :all do
148
+ puts @var # => 1
149
+ end
150
+ end
151
+ ```
152
+
153
+ #### Recognizing Path
154
+
155
+ Grape now considers the types of the configured `route_params` in order to determine the endpoint that matches with the performed request.
156
+
157
+ So taking into account this `Grape::API` class
158
+
159
+ ```ruby
160
+ class Books < Grape::API
161
+ resource :books do
162
+ route_param :id, type: Integer do
163
+ # GET /books/:id
164
+ get do
165
+ #...
166
+ end
167
+ end
168
+
169
+ resource :share do
170
+ # POST /books/share
171
+ post do
172
+ # ....
173
+ end
174
+ end
175
+ end
176
+ end
177
+ ```
178
+
179
+ Before:
180
+ ```ruby
181
+ API.recognize_path '/books/1' # => /books/:id
182
+ API.recognize_path '/books/share' # => /books/:id
183
+ API.recognize_path '/books/other' # => /books/:id
184
+ ```
185
+
186
+ After:
187
+ ```ruby
188
+ API.recognize_path '/books/1' # => /books/:id
189
+ API.recognize_path '/books/share' # => /books/share
190
+ API.recognize_path '/books/other' # => nil
191
+ ```
192
+
193
+ This implies that before this changes, when you performed `/books/other` and it matched with the `/books/:id` endpoint, you get a `400 Bad Request` response because the type of the provided `:id` param was not an `Integer`. However, after upgrading to version `2.1.0` you will get a `404 Not Found` response, because there is not a defined endpoint that matches with `/books/other`.
194
+
195
+ See [#2379](https://github.com/ruby-grape/grape/pull/2379) for more information.
196
+
197
+ ### Upgrading to >= 2.0.0
198
+
199
+ #### Headers
200
+
201
+ As per [rack/rack#1592](https://github.com/rack/rack/issues/1592) Rack 3 is following the HTTP/2+ semantics which require header names to be lower case. To avoid compatibility issues, starting with Grape 1.9.0, headers will be cased based on what version of Rack you are using.
202
+
203
+ Given this request:
204
+
205
+ ```shell
206
+ curl -H "Content-Type: application/json" -H "Secret-Password: foo" ...
207
+ ```
208
+
209
+ If you are using Rack 3 in your application then the headers will be set to:
210
+
211
+ ```ruby
212
+ { "content-type" => "application/json", "secret-password" => "foo"}
213
+ ```
214
+
215
+ This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
216
+
217
+ ```ruby
218
+ get do
219
+ # This would use headers['Secret-Password'] in Rack < 3
220
+ error!('Unauthorized', 401) unless headers['secret-password'] == 'swordfish'
221
+ end
222
+ ```
223
+
224
+ See [#2355](https://github.com/ruby-grape/grape/pull/2355) for more information.
225
+
226
+ #### Digest auth deprecation
227
+
228
+ Digest auth has been removed along with the deprecation of `Rack::Auth::Digest` in Rack 3.
229
+
230
+ See [#2294](https://github.com/ruby-grape/grape/issues/2294) for more information.
231
+
4
232
  ### Upgrading to >= 1.7.0
5
233
 
6
234
  #### Exceptions renaming
@@ -439,8 +667,7 @@ end
439
667
 
440
668
  ##### `name` (and other caveats) of the mounted API
441
669
 
442
- After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
443
- which inherit from `Grape::API::Instance`.
670
+ After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class which inherit from `Grape::API::Instance`.
444
671
 
445
672
  What this means in practice, is:
446
673
 
@@ -820,8 +1047,7 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
820
1047
 
821
1048
  #### Bypasses formatters when status code indicates no content
822
1049
 
823
- To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
824
- be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
1050
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
825
1051
 
826
1052
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
827
1053
 
@@ -1262,8 +1488,7 @@ As replacement can be used
1262
1488
  * `Grape::Middleware::Auth::Digest` => [`Rack::Auth::Digest::MD5`](https://github.com/rack/rack/blob/master/lib/rack/auth/digest/md5.rb)
1263
1489
  * `Grape::Middleware::Auth::OAuth2` => [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2)
1264
1490
 
1265
- If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/ruby-grape/grape/tree/v0.7.0/lib/grape/middleware/auth)
1266
- and host these files within your application
1491
+ If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/ruby-grape/grape/tree/v0.7.0/lib/grape/middleware/auth) and host these files within your application
1267
1492
 
1268
1493
  See [#703](https://github.com/ruby-grape/Grape/pull/703) for more information.
1269
1494
 
data/grape.gemspec CHANGED
@@ -20,17 +20,13 @@ Gem::Specification.new do |s|
20
20
  'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
21
21
  }
22
22
 
23
- s.add_runtime_dependency 'activesupport', '>= 5'
24
- s.add_runtime_dependency 'builder'
23
+ s.add_runtime_dependency 'activesupport', '>= 6'
25
24
  s.add_runtime_dependency 'dry-types', '>= 1.1'
26
- s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
27
- s.add_runtime_dependency 'rack', '>= 1.3.0'
28
- s.add_runtime_dependency 'rack-accept'
25
+ s.add_runtime_dependency 'mustermann-grape', '~> 1.1.0'
26
+ s.add_runtime_dependency 'rack', '>= 2'
27
+ s.add_runtime_dependency 'zeitwerk'
29
28
 
30
- s.files = %w[CHANGELOG.md CONTRIBUTING.md README.md grape.png UPGRADING.md LICENSE]
31
- s.files += %w[grape.gemspec]
32
- s.files += Dir['lib/**/*']
33
- s.test_files = Dir['spec/**/*']
29
+ s.files = Dir['lib/**/*', 'CHANGELOG.md', 'CONTRIBUTING.md', 'README.md', 'grape.png', 'UPGRADING.md', 'LICENSE', 'grape.gemspec']
34
30
  s.require_paths = ['lib']
35
- s.required_ruby_version = '>= 2.6.0'
31
+ s.required_ruby_version = '>= 2.7.0'
36
32
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router'
4
-
5
3
  module Grape
6
4
  class API
7
5
  # The API Instance class, is the engine behind Grape::API. Each class that inherits
@@ -112,7 +110,7 @@ module Grape
112
110
  end
113
111
 
114
112
  def evaluate_as_instance_with_configuration(block, lazy: false)
115
- lazy_block = Grape::Util::LazyBlock.new do |configuration|
113
+ lazy_block = Grape::Util::Lazy::Block.new do |configuration|
116
114
  value_for_configuration = configuration
117
115
  self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
118
116
  response = instance_eval(&block)
@@ -127,6 +125,7 @@ module Grape
127
125
  end
128
126
 
129
127
  def inherited(subclass)
128
+ super
130
129
  subclass.reset!
131
130
  subclass.logger = logger.clone
132
131
  end
@@ -162,9 +161,13 @@ module Grape
162
161
 
163
162
  # Handle a request. See Rack documentation for what `env` is.
164
163
  def call(env)
165
- result = @router.call(env)
166
- result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
167
- result
164
+ status, headers, response = @router.call(env)
165
+ unless cascade?
166
+ headers = Grape::Util::Header.new.merge(headers)
167
+ headers.delete(Grape::Http::Headers::X_CASCADE)
168
+ end
169
+
170
+ [status, headers, response]
168
171
  end
169
172
 
170
173
  # Some requests may return a HTTP 404 error if grape cannot find a matching
@@ -203,11 +206,11 @@ module Grape
203
206
 
204
207
  allowed_methods = config[:methods].dup
205
208
 
206
- allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET)
209
+ allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
207
210
 
208
- allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods)
211
+ allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods)
209
212
 
210
- config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
213
+ config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
211
214
 
212
215
  attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
213
216
  generate_not_allowed_method(config[:pattern], **attributes)
@@ -218,7 +221,7 @@ module Grape
218
221
 
219
222
  def collect_route_config_per_pattern
220
223
  all_routes = self.class.endpoints.map(&:routes).flatten
221
- routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp }
224
+ routes_by_regexp = all_routes.group_by(&:pattern_regexp)
222
225
 
223
226
  # Build the configuration based on the first endpoint and the collection of methods supported.
224
227
  routes_by_regexp.values.map do |routes|
data/lib/grape/api.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router'
4
- require 'grape/api/instance'
5
-
6
3
  module Grape
7
4
  # The API class is the primary entry point for creating Grape APIs. Users
8
5
  # should subclass this class in order to build an API.
@@ -26,8 +23,8 @@ module Grape
26
23
  attr_accessor :base_instance, :instances
27
24
 
28
25
  # Rather than initializing an object of type Grape::API, create an object of type Instance
29
- def new(*args, &block)
30
- base_instance.new(*args, &block)
26
+ def new(...)
27
+ base_instance.new(...)
31
28
  end
32
29
 
33
30
  # When inherited, will create a list of all instances (times the API was mounted)
@@ -77,8 +74,8 @@ module Grape
77
74
  # the headers, and the body. See [the rack specification]
78
75
  # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
79
76
  # NOTE: This will only be called on an API directly mounted on RACK
80
- def call(*args, &block)
81
- instance_for_rack.call(*args, &block)
77
+ def call(...)
78
+ instance_for_rack.call(...)
82
79
  end
83
80
 
84
81
  # Alleviates problems with autoloading by tring to search for the constant
@@ -128,7 +125,6 @@ module Grape
128
125
  end
129
126
 
130
127
  def compile!
131
- require 'grape/eager_load'
132
128
  instance_for_rack.compile! # See API::Instance.compile!
133
129
  end
134
130
 
@@ -150,6 +146,19 @@ module Grape
150
146
  @instances.each do |instance|
151
147
  last_response = replay_step_on(instance, setup_step)
152
148
  end
149
+
150
+ # Updating all previously mounted classes in the case that new methods have been executed.
151
+ if method != :mount && @setup.any?
152
+ previous_mount_steps = @setup.select { |step| step[:method] == :mount }
153
+ previous_mount_steps.each do |mount_step|
154
+ refresh_mount_step = mount_step.merge(method: :refresh_mounted_api)
155
+ @setup += [refresh_mount_step]
156
+ @instances.each do |instance|
157
+ replay_step_on(instance, refresh_mount_step)
158
+ end
159
+ end
160
+ end
161
+
153
162
  last_response
154
163
  end
155
164
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/util/registrable'
4
-
5
3
  module Grape
6
4
  module ContentTypes
7
5
  extend Util::Registrable
data/lib/grape/cookies.rb CHANGED
@@ -33,9 +33,10 @@ module Grape
33
33
  @cookies.each(&block)
34
34
  end
35
35
 
36
+ # see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
36
37
  # rubocop:disable Layout/SpaceBeforeBrackets
37
38
  def delete(name, **opts)
38
- options = opts.merge(value: 'deleted', expires: Time.at(0))
39
+ options = opts.merge(max_age: '0', value: '', expires: Time.at(0))
39
40
  self.[]=(name, options)
40
41
  end
41
42
  # rubocop:enable Layout/SpaceBeforeBrackets
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry-types'
4
-
5
3
  module Grape
6
4
  module DryTypes
7
5
  # Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
@@ -5,6 +5,27 @@ module Grape
5
5
  module Desc
6
6
  include Grape::DSL::Settings
7
7
 
8
+ ROUTE_ATTRIBUTES = %i[
9
+ body_name
10
+ consumes
11
+ default
12
+ deprecated
13
+ description
14
+ detail
15
+ entity
16
+ headers
17
+ hidden
18
+ http_codes
19
+ is_array
20
+ named
21
+ nickname
22
+ params
23
+ produces
24
+ security
25
+ summary
26
+ tags
27
+ ].freeze
28
+
8
29
  # Add a description to the next namespace or function.
9
30
  # @param description [String] descriptive string for this endpoint
10
31
  # or namespace
@@ -68,7 +89,7 @@ module Grape
68
89
  end
69
90
 
70
91
  config_class.configure(&config_block)
71
- ActiveSupport::Deprecation.warn('Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.') if options.any?
92
+ Grape.deprecator.warn('Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.') if options.any?
72
93
  options = config_class.settings
73
94
  else
74
95
  options = options.merge(description: description)
@@ -81,26 +102,7 @@ module Grape
81
102
  # Returns an object which configures itself via an instance-context DSL.
82
103
  def desc_container(endpoint_configuration)
83
104
  Module.new do
84
- include Grape::Util::StrictHashConfiguration.module(
85
- :summary,
86
- :description,
87
- :detail,
88
- :params,
89
- :entity,
90
- :http_codes,
91
- :named,
92
- :body_name,
93
- :headers,
94
- :hidden,
95
- :deprecated,
96
- :is_array,
97
- :nickname,
98
- :produces,
99
- :consumes,
100
- :security,
101
- :tags,
102
- :default
103
- )
105
+ include Grape::Util::StrictHashConfiguration.module(*ROUTE_ATTRIBUTES)
104
106
  config_context.define_singleton_method(:configuration) do
105
107
  endpoint_configuration
106
108
  end
@@ -12,7 +12,7 @@ module Grape
12
12
  if key
13
13
  val ? header[key.to_s] = val : header.delete(key.to_s)
14
14
  else
15
- @header ||= {}
15
+ @header ||= Grape::Util::Header.new
16
16
  end
17
17
  end
18
18
  alias headers header
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/dsl/headers'
4
-
5
3
  module Grape
6
4
  module DSL
7
5
  module InsideRoute
@@ -31,11 +29,17 @@ module Grape
31
29
  options = options.reverse_merge(include_missing: true, include_parent_namespaces: true, evaluate_given: false)
32
30
  declared_params ||= optioned_declared_params(**options)
33
31
 
34
- if passed_params.is_a?(Array)
35
- declared_array(passed_params, options, declared_params, params_nested_path)
36
- else
37
- declared_hash(passed_params, options, declared_params, params_nested_path)
32
+ res = if passed_params.is_a?(Array)
33
+ declared_array(passed_params, options, declared_params, params_nested_path)
34
+ else
35
+ declared_hash(passed_params, options, declared_params, params_nested_path)
36
+ end
37
+
38
+ if (key_maps = namespace_stackable(:contract_key_map))
39
+ key_maps.each { |key_map| key_map.write(passed_params, res) }
38
40
  end
41
+
42
+ res
39
43
  end
40
44
 
41
45
  private
@@ -99,7 +103,7 @@ module Grape
99
103
 
100
104
  route_options_params = options[:route_options][:params] || {}
101
105
  type = route_options_params.dig(key, :type)
102
- has_children = route_options_params.keys.any? { |k| k != key && k.start_with?(key) }
106
+ has_children = route_options_params.keys.any? { |k| k != key && k.start_with?("#{key}[") }
103
107
 
104
108
  if type == 'Hash' && !has_children
105
109
  {}
@@ -162,9 +166,27 @@ module Grape
162
166
  # @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
163
167
  # @param additional_headers [Hash] Addtional headers for the response.
164
168
  def error!(message, status = nil, additional_headers = nil)
165
- self.status(status || namespace_inheritable(:default_error_status))
169
+ status = self.status(status || namespace_inheritable(:default_error_status))
166
170
  headers = additional_headers.present? ? header.merge(additional_headers) : header
167
- throw :error, message: message, status: self.status, headers: headers
171
+ throw :error, message: message, status: status, headers: headers
172
+ end
173
+
174
+ # Creates a Rack response based on the provided message, status, and headers.
175
+ # The content type in the headers is set to the default content type unless provided.
176
+ # The message is HTML-escaped if the content type is 'text/html'.
177
+ #
178
+ # @param message [String] The content of the response.
179
+ # @param status [Integer] The HTTP status code.
180
+ # @params headers [Hash] (optional) Headers for the response
181
+ # (default: {Rack::CONTENT_TYPE => content_type}).
182
+ #
183
+ # Returns:
184
+ # A Rack::Response object containing the specified message, status, and headers.
185
+ #
186
+ def rack_response(message, status = 200, headers = { Rack::CONTENT_TYPE => content_type })
187
+ Grape.deprecator.warn('The rack_response method has been deprecated, use error! instead.')
188
+ message = Rack::Utils.escape_html(message) if headers[Rack::CONTENT_TYPE] == 'text/html'
189
+ Rack::Response.new(Array.wrap(message), Rack::Utils.status_code(status), headers)
168
190
  end
169
191
 
170
192
  # Redirect to a new url.
@@ -178,14 +200,14 @@ module Grape
178
200
  if permanent
179
201
  status 301
180
202
  body_message ||= "This resource has been moved permanently to #{url}."
181
- elsif env[Grape::Http::Headers::HTTP_VERSION] == 'HTTP/1.1' && request.request_method.to_s.upcase != Grape::Http::Headers::GET
203
+ elsif http_version == 'HTTP/1.1' && !request.get?
182
204
  status 303
183
205
  body_message ||= "An alternate resource is located at #{url}."
184
206
  else
185
207
  status 302
186
208
  body_message ||= "This resource has been moved temporarily to #{url}."
187
209
  end
188
- header 'Location', url
210
+ header Grape::Http::Headers::LOCATION, url
189
211
  content_type 'text/plain'
190
212
  body body_message
191
213
  end
@@ -204,10 +226,9 @@ module Grape
204
226
  when nil
205
227
  return @status if instance_variable_defined?(:@status) && @status
206
228
 
207
- case request.request_method.to_s.upcase
208
- when Grape::Http::Headers::POST
229
+ if request.post?
209
230
  201
210
- when Grape::Http::Headers::DELETE
231
+ elsif request.delete?
211
232
  if instance_variable_defined?(:@body) && @body.present?
212
233
  200
213
234
  else
@@ -224,9 +245,9 @@ module Grape
224
245
  # Set response content-type
225
246
  def content_type(val = nil)
226
247
  if val
227
- header(Grape::Http::Headers::CONTENT_TYPE, val)
248
+ header(Rack::CONTENT_TYPE, val)
228
249
  else
229
- header[Grape::Http::Headers::CONTENT_TYPE]
250
+ header[Rack::CONTENT_TYPE]
230
251
  end
231
252
  end
232
253
 
@@ -280,13 +301,13 @@ module Grape
280
301
  # Deprecated method to send files to the client. Use `sendfile` or `stream`
281
302
  def file(value = nil)
282
303
  if value.is_a?(String)
283
- ActiveSupport::Deprecation.warn('Use sendfile or stream to send files.')
304
+ Grape.deprecator.warn('Use sendfile or stream to send files.')
284
305
  sendfile(value)
285
306
  elsif !value.is_a?(NilClass)
286
- ActiveSupport::Deprecation.warn('Use stream to use a Stream object.')
307
+ Grape.deprecator.warn('Use stream to use a Stream object.')
287
308
  stream(value)
288
309
  else
289
- ActiveSupport::Deprecation.warn('Use sendfile or stream to send files.')
310
+ Grape.deprecator.warn('Use sendfile or stream to send files.')
290
311
  sendfile
291
312
  end
292
313
  end
@@ -328,9 +349,9 @@ module Grape
328
349
  def stream(value = nil)
329
350
  return if value.nil? && @stream.nil?
330
351
 
331
- header 'Content-Length', nil
332
- header 'Transfer-Encoding', nil
333
- header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
352
+ header Rack::CONTENT_LENGTH, nil
353
+ header Grape::Http::Headers::TRANSFER_ENCODING, nil
354
+ header Rack::CACHE_CONTROL, 'no-cache' # Skips ETag generation (reading the response up front)
334
355
  if value.is_a?(String)
335
356
  file_body = Grape::ServeStream::FileBody.new(value)
336
357
  @stream = Grape::ServeStream::StreamResponse.new(file_body)
@@ -436,6 +457,10 @@ module Grape
436
457
  embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
437
458
  entity_class.represent(object, **embeds.merge(options))
438
459
  end
460
+
461
+ def http_version
462
+ env['HTTP_VERSION'] || env[Rack::SERVER_PROTOCOL]
463
+ end
439
464
  end
440
465
  end
441
466
  end