grape 1.0.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +346 -39
- data/LICENSE +1 -1
- data/README.md +658 -90
- data/UPGRADING.md +472 -17
- data/grape.gemspec +17 -6
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +279 -0
- data/lib/grape/api.rb +132 -176
- data/lib/grape/config.rb +34 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +4 -0
- data/lib/grape/dsl/api.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +22 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +44 -12
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +11 -6
- data/lib/grape/dsl/inside_route.rb +116 -36
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +12 -3
- data/lib/grape/dsl/parameters.rb +34 -16
- data/lib/grape/dsl/request_response.rb +13 -8
- data/lib/grape/dsl/routing.rb +19 -12
- data/lib/grape/dsl/settings.rb +22 -4
- data/lib/grape/dsl/validations.rb +24 -4
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +66 -57
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +6 -4
- data/lib/grape/error_formatter/txt.rb +10 -3
- data/lib/grape/error_formatter/xml.rb +6 -4
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/exceptions/base.rb +22 -14
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -1
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -1
- data/lib/grape/exceptions/invalid_formatter.rb +2 -1
- data/lib/grape/exceptions/invalid_message_body.rb +2 -1
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -1
- data/lib/grape/exceptions/method_not_allowed.rb +2 -1
- data/lib/grape/exceptions/missing_group_type.rb +2 -1
- data/lib/grape/exceptions/missing_mime_type.rb +2 -1
- data/lib/grape/exceptions/missing_option.rb +2 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -1
- data/lib/grape/exceptions/unknown_options.rb +2 -1
- data/lib/grape/exceptions/unknown_parameter.rb +2 -1
- data/lib/grape/exceptions/unknown_validator.rb +2 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +2 -1
- data/lib/grape/exceptions/validation.rb +5 -4
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +16 -13
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
- data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
- data/lib/grape/extensions/hash.rb +2 -0
- data/lib/grape/extensions/hashie/mash.rb +2 -0
- data/lib/grape/formatter/json.rb +3 -0
- data/lib/grape/formatter/serializable_hash.rb +4 -1
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +3 -0
- data/lib/grape/formatter.rb +5 -3
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/locale/en.yml +3 -1
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +2 -0
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +12 -7
- data/lib/grape/middleware/error.rb +75 -61
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +17 -13
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +15 -5
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
- data/lib/grape/middleware/versioner/header.rb +13 -9
- data/lib/grape/middleware/versioner/param.rb +4 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
- data/lib/grape/middleware/versioner/path.rb +5 -1
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/namespace.rb +15 -3
- data/lib/grape/parser/json.rb +3 -1
- data/lib/grape/parser/xml.rb +3 -1
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/path.rb +16 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +19 -9
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +22 -18
- data/lib/grape/router/route.rb +16 -30
- data/lib/grape/router.rb +37 -28
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +3 -3
- data/lib/grape/util/inheritable_values.rb +7 -25
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_block.rb +27 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +99 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +10 -35
- data/lib/grape/util/stackable_values.rb +21 -34
- data/lib/grape/util/strict_hash_configuration.rb +2 -0
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations/attributes_iterator.rb +16 -6
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +128 -66
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types/array_coercer.rb +65 -0
- data/lib/grape/validations/types/build_coercer.rb +52 -46
- data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +47 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +67 -0
- data/lib/grape/validations/types/set_coercer.rb +40 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/types.rb +26 -38
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none.rb +8 -12
- data/lib/grape/validations/validators/allow_blank.rb +4 -2
- data/lib/grape/validations/validators/as.rb +12 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -12
- data/lib/grape/validations/validators/base.rb +23 -15
- data/lib/grape/validations/validators/coerce.rb +47 -28
- data/lib/grape/validations/validators/default.rb +7 -6
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -22
- data/lib/grape/validations/validators/except_values.rb +4 -2
- data/lib/grape/validations/validators/multiple_params_base.rb +19 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -17
- data/lib/grape/validations/validators/presence.rb +4 -1
- data/lib/grape/validations/validators/regexp.rb +5 -2
- data/lib/grape/validations/validators/same_as.rb +27 -0
- data/lib/grape/validations/validators/values.rb +29 -9
- data/lib/grape/validations.rb +2 -0
- data/lib/grape/version.rb +3 -1
- data/lib/grape.rb +107 -73
- data/spec/grape/api/custom_validations_spec.rb +6 -3
- data/spec/grape/api/deeply_included_options_spec.rb +2 -0
- data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
- data/spec/grape/api/inherited_helpers_spec.rb +116 -0
- data/spec/grape/api/instance_spec.rb +104 -0
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/nested_helpers_spec.rb +2 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/parameters_modification_spec.rb +3 -1
- data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +2 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +49 -0
- data/spec/grape/api/shared_helpers_spec.rb +2 -0
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +775 -65
- data/spec/grape/config_spec.rb +19 -0
- data/spec/grape/dsl/callbacks_spec.rb +3 -1
- data/spec/grape/dsl/configuration_spec.rb +2 -0
- data/spec/grape/dsl/desc_spec.rb +42 -16
- data/spec/grape/dsl/headers_spec.rb +2 -0
- data/spec/grape/dsl/helpers_spec.rb +23 -2
- data/spec/grape/dsl/inside_route_spec.rb +185 -34
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +11 -1
- data/spec/grape/dsl/parameters_spec.rb +12 -9
- data/spec/grape/dsl/request_response_spec.rb +2 -0
- data/spec/grape/dsl/routing_spec.rb +13 -1
- data/spec/grape/dsl/settings_spec.rb +4 -2
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint/declared_spec.rb +848 -0
- data/spec/grape/endpoint_spec.rb +75 -515
- data/spec/grape/entity_spec.rb +19 -11
- data/spec/grape/exceptions/base_spec.rb +67 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -1
- data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -1
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
- data/spec/grape/exceptions/missing_option_spec.rb +2 -1
- data/spec/grape/exceptions/unknown_options_spec.rb +3 -2
- data/spec/grape/exceptions/unknown_validator_spec.rb +2 -1
- data/spec/grape/exceptions/validation_errors_spec.rb +17 -4
- data/spec/grape/exceptions/validation_spec.rb +3 -1
- data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
- data/spec/grape/integration/rack_spec.rb +25 -7
- data/spec/grape/loading_spec.rb +2 -0
- data/spec/grape/middleware/auth/base_spec.rb +2 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +6 -4
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +10 -0
- data/spec/grape/middleware/error_spec.rb +4 -3
- data/spec/grape/middleware/exception_spec.rb +205 -55
- data/spec/grape/middleware/formatter_spec.rb +130 -15
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +15 -1
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +10 -2
- data/spec/grape/middleware/versioner/param_spec.rb +4 -2
- data/spec/grape/middleware/versioner/path_spec.rb +4 -2
- data/spec/grape/middleware/versioner_spec.rb +2 -0
- data/spec/grape/named_api_spec.rb +21 -0
- data/spec/grape/parser_spec.rb +7 -5
- data/spec/grape/path_spec.rb +9 -7
- data/spec/grape/presenters/presenter_spec.rb +2 -0
- data/spec/grape/request_spec.rb +26 -0
- data/spec/grape/util/inheritable_setting_spec.rb +4 -2
- data/spec/grape/util/inheritable_values_spec.rb +2 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +15 -13
- data/spec/grape/util/stackable_values_spec.rb +16 -14
- data/spec/grape/util/strict_hash_configuration_spec.rb +3 -1
- data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
- data/spec/grape/validations/params_scope_spec.rb +364 -7
- data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +9 -36
- data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
- data/spec/grape/validations/validators/coerce_spec.rb +558 -133
- data/spec/grape/validations/validators/default_spec.rb +212 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
- data/spec/grape/validations/validators/except_values_spec.rb +6 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
- data/spec/grape/validations/validators/presence_spec.rb +30 -0
- data/spec/grape/validations/validators/regexp_spec.rb +2 -0
- data/spec/grape/validations/validators/same_as_spec.rb +65 -0
- data/spec/grape/validations/validators/values_spec.rb +73 -19
- data/spec/grape/validations_spec.rb +403 -53
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -0
- data/spec/integration/multi_xml/xml_spec.rb +2 -0
- data/spec/shared/versioning_examples.rb +78 -18
- data/spec/spec_helper.rb +13 -2
- data/spec/support/basic_auth_encode_helpers.rb +3 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +3 -1
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +8 -8
- metadata +176 -130
- data/Appraisals +0 -32
- data/Dangerfile +0 -1
- data/Gemfile +0 -34
- data/Gemfile.lock +0 -229
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -70
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -36
- data/gemfiles/multi_xml.gemfile +0 -36
- data/gemfiles/rack_1.5.2.gemfile +0 -36
- data/gemfiles/rack_edge.gemfile +0 -36
- data/gemfiles/rails_3.gemfile +0 -37
- data/gemfiles/rails_4.gemfile +0 -36
- data/gemfiles/rails_5.gemfile +0 -36
- data/gemfiles/rails_edge.gemfile +0 -36
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
- data/lib/grape/util/content_types.rb +0 -26
- data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
data/UPGRADING.md
CHANGED
@@ -1,6 +1,472 @@
|
|
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
|
+
|
93
|
+
### Upgrading to >= 1.5.0
|
94
|
+
|
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 `{}`.
|
96
|
+
|
97
|
+
In 1.5.0 this behavior is reverted, so the whole params structure will always be available via `declared`, regardless of whether any params are passed.
|
98
|
+
|
99
|
+
The following rules now apply to the `declared` helper when params are missing and `include_missing=true`:
|
100
|
+
|
101
|
+
* Hash params with children will resolve to a Hash with keys for each declared child.
|
102
|
+
* Hash params with no children will resolve to `{}`.
|
103
|
+
* Set params will resolve to `Set.new`.
|
104
|
+
* Array params will resolve to `[]`.
|
105
|
+
* All other params will resolve to `nil`.
|
106
|
+
|
107
|
+
#### Example
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
class Api < Grape::API
|
111
|
+
params do
|
112
|
+
optional :outer, type: Hash do
|
113
|
+
optional :inner, type: Hash do
|
114
|
+
optional :value, type: String
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
get 'example' do
|
119
|
+
declared(params, include_missing: true)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
```
|
125
|
+
get '/example'
|
126
|
+
# 1.3.3 = {}
|
127
|
+
# 1.5.0 = {outer: {inner: {value:null}}}
|
128
|
+
```
|
129
|
+
|
130
|
+
For more information see [#2103](https://github.com/ruby-grape/grape/pull/2103).
|
131
|
+
|
132
|
+
### Upgrading to >= 1.4.0
|
133
|
+
|
134
|
+
#### Reworking stream and file and un-deprecating stream like-objects
|
135
|
+
|
136
|
+
Previously in 0.16 stream-like objects were deprecated. This release restores their functionality for use-cases other than file streaming.
|
137
|
+
|
138
|
+
This release deprecated `file` in favor of `sendfile` to better document its purpose.
|
139
|
+
|
140
|
+
To deliver a file via the Sendfile support in your web server and have the Rack::Sendfile middleware enabled. See [`Rack::Sendfile`](https://www.rubydoc.info/gems/rack/Rack/Sendfile).
|
141
|
+
```ruby
|
142
|
+
class API < Grape::API
|
143
|
+
get '/' do
|
144
|
+
sendfile '/path/to/file'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
Use `stream` to stream file content in chunks.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
class API < Grape::API
|
153
|
+
get '/' do
|
154
|
+
stream '/path/to/file'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
Or use `stream` to stream other kinds of content. In the following example a streamer class
|
160
|
+
streams paginated data from a database.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class MyObject
|
164
|
+
attr_accessor :result
|
165
|
+
|
166
|
+
def initialize(query)
|
167
|
+
@result = query
|
168
|
+
end
|
169
|
+
|
170
|
+
def each
|
171
|
+
yield '['
|
172
|
+
# Do paginated DB fetches and return each page formatted
|
173
|
+
first = false
|
174
|
+
result.find_in_batches do |records|
|
175
|
+
yield process_records(records, first)
|
176
|
+
first = false
|
177
|
+
end
|
178
|
+
yield ']'
|
179
|
+
end
|
180
|
+
|
181
|
+
def process_records(records, first)
|
182
|
+
buffer = +''
|
183
|
+
buffer << ',' unless first
|
184
|
+
buffer << records.map(&:to_json).join(',')
|
185
|
+
buffer
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class API < Grape::API
|
190
|
+
get '/' do
|
191
|
+
stream MyObject.new(Sprocket.all)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
### Upgrading to >= 1.3.3
|
197
|
+
|
198
|
+
#### Nil values for structures
|
199
|
+
|
200
|
+
Nil values always been a special case when dealing with types especially with the following structures:
|
201
|
+
|
202
|
+
- Array
|
203
|
+
- Hash
|
204
|
+
- Set
|
205
|
+
|
206
|
+
The behavior for these structures has change through out the latest releases. For example:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
class Api < Grape::API
|
210
|
+
params do
|
211
|
+
require :my_param, type: Array[Integer]
|
212
|
+
end
|
213
|
+
|
214
|
+
get 'example' do
|
215
|
+
params[:my_param]
|
216
|
+
end
|
217
|
+
get '/example', params: { my_param: nil }
|
218
|
+
# 1.3.1 = []
|
219
|
+
# 1.3.2 = nil
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
223
|
+
For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
|
224
|
+
|
225
|
+
If you want to have the same behavior as 1.3.1, apply a `default` validator:
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
class Api < Grape::API
|
229
|
+
params do
|
230
|
+
require :my_param, type: Array[Integer], default: []
|
231
|
+
end
|
232
|
+
|
233
|
+
get 'example' do
|
234
|
+
params[:my_param]
|
235
|
+
end
|
236
|
+
get '/example', params: { my_param: nil } # => []
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
#### Default validator
|
241
|
+
|
242
|
+
Default validator is now applied for `nil` values.
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
class Api < Grape::API
|
246
|
+
params do
|
247
|
+
requires :my_param, type: Integer, default: 0
|
248
|
+
end
|
249
|
+
|
250
|
+
get 'example' do
|
251
|
+
params[:my_param]
|
252
|
+
end
|
253
|
+
get '/example', params: { my_param: nil } #=> before: nil, after: 0
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
### Upgrading to >= 1.3.0
|
258
|
+
|
259
|
+
You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
|
260
|
+
|
261
|
+
#### Ruby
|
262
|
+
|
263
|
+
After adding dry-types, Ruby 2.4 or newer is required.
|
264
|
+
|
265
|
+
#### Coercion
|
266
|
+
|
267
|
+
[Virtus](https://github.com/solnic/virtus) has been replaced by [dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter coercion. If your project depends on Virtus outside of Grape, explicitly add it to your `Gemfile`.
|
268
|
+
|
269
|
+
Here's an example of how to migrate a custom type from Virtus to dry-types:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
# Legacy Grape parser
|
273
|
+
class SecureUriType < Virtus::Attribute
|
274
|
+
def coerce(input)
|
275
|
+
URI.parse value
|
276
|
+
end
|
277
|
+
|
278
|
+
def value_coerced?(input)
|
279
|
+
value.is_a? String
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
params do
|
284
|
+
requires :secure_uri, type: SecureUri
|
285
|
+
end
|
286
|
+
```
|
287
|
+
|
288
|
+
To use dry-types, we need to:
|
289
|
+
|
290
|
+
1. Remove the inheritance of `Virtus::Attribute`
|
291
|
+
1. Rename `coerce` to `self.parse`
|
292
|
+
1. Rename `value_coerced?` to `self.parsed?`
|
293
|
+
|
294
|
+
The custom type must have a class-level `parse` method to the model. A class-level `parsed?` is needed if the parsed type differs from the defined type. In the example below, since `SecureUri` is not the same as `URI::HTTPS`, `self.parsed?` is needed:
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
# New dry-types parser
|
298
|
+
class SecureUri
|
299
|
+
def self.parse(value)
|
300
|
+
URI.parse value
|
301
|
+
end
|
302
|
+
|
303
|
+
def self.parsed?(value)
|
304
|
+
value.is_a? URI::HTTPS
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
params do
|
309
|
+
requires :secure_uri, type: SecureUri
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
313
|
+
#### Coercing to `FalseClass` or `TrueClass` no longer works
|
314
|
+
|
315
|
+
Previous Grape versions allowed this, though it wasn't documented:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
requires :true_value, type: TrueClass
|
319
|
+
requires :bool_value, types: [FalseClass, TrueClass]
|
320
|
+
```
|
321
|
+
|
322
|
+
This is no longer supported, if you do this, your values will never be valid. Instead you should do this:
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
requires :true_value, type: Boolean # in your endpoint you should validate if this is actually `true`
|
326
|
+
requires :bool_value, type: Boolean
|
327
|
+
```
|
328
|
+
|
329
|
+
#### Ensure that Array types have explicit coercions
|
330
|
+
|
331
|
+
Unlike Virtus, dry-types does not perform any implict coercions. If you have any uses of `Array[String]`, `Array[Integer]`, etc. be sure they use a `coerce_with` block. For example:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
requires :values, type: Array[String]
|
335
|
+
```
|
336
|
+
|
337
|
+
It's quite common to pass a comma-separated list, such as `tag1,tag2` as `values`. Previously Virtus would implicitly coerce this to `Array(values)` so that `["tag1,tag2"]` would pass the type checks, but with `dry-types` the values are no longer coerced for you. To fix this, you might do:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
|
341
|
+
```
|
342
|
+
|
343
|
+
Likewise, for `Array[Integer]`, you might do:
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(',').map(&:strip).map(&:to_i) }
|
347
|
+
```
|
348
|
+
|
349
|
+
For more information see [#1920](https://github.com/ruby-grape/grape/pull/1920).
|
350
|
+
|
351
|
+
### Upgrading to >= 1.2.4
|
352
|
+
|
353
|
+
#### Headers in `error!` call
|
354
|
+
|
355
|
+
Headers in `error!` will be merged with `headers` hash. If any header need to be cleared on `error!` call, make sure to move it to the `after` block.
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
class SampleApi < Grape::API
|
359
|
+
before do
|
360
|
+
header 'X-Before-Header', 'before_call'
|
361
|
+
end
|
362
|
+
|
363
|
+
get 'ping' do
|
364
|
+
header 'X-App-Header', 'on_call'
|
365
|
+
error! :pong, 400, 'X-Error-Details' => 'Invalid token'
|
366
|
+
end
|
367
|
+
end
|
368
|
+
```
|
369
|
+
**Former behaviour**
|
370
|
+
```ruby
|
371
|
+
response.headers['X-Before-Header'] # => nil
|
372
|
+
response.headers['X-App-Header'] # => nil
|
373
|
+
response.headers['X-Error-Details'] # => Invalid token
|
374
|
+
```
|
375
|
+
|
376
|
+
**Current behaviour**
|
377
|
+
```ruby
|
378
|
+
response.headers['X-Before-Header'] # => 'before_call'
|
379
|
+
response.headers['X-App-Header'] # => 'on_call'
|
380
|
+
response.headers['X-Error-Details'] # => Invalid token
|
381
|
+
```
|
382
|
+
|
383
|
+
### Upgrading to >= 1.2.1
|
384
|
+
|
385
|
+
#### Obtaining the name of a mounted class
|
386
|
+
|
387
|
+
In order to make obtaining the name of a mounted class simpler, we've delegated `.to_s` to `base.name`
|
388
|
+
|
389
|
+
**Deprecated in 1.2.0**
|
390
|
+
```ruby
|
391
|
+
payload[:endpoint].options[:for].name
|
392
|
+
```
|
393
|
+
**New**
|
394
|
+
```ruby
|
395
|
+
payload[:endpoint].options[:for].to_s
|
396
|
+
```
|
397
|
+
|
398
|
+
### Upgrading to >= 1.2.0
|
399
|
+
|
400
|
+
#### Changes in the Grape::API class
|
401
|
+
|
402
|
+
##### Patching the class
|
403
|
+
|
404
|
+
In an effort to make APIs re-mountable, The class `Grape::API` no longer refers to an API instance, rather, what used to be `Grape::API` is now `Grape::API::Instance` and `Grape::API` was replaced with a class that can contain several instances of `Grape::API`.
|
405
|
+
|
406
|
+
This changes were done in such a way that no code-changes should be required. However, if experiencing problems, or relying on private methods and internal behaviour too deeply, it is possible to restore the prior behaviour by replacing the references from `Grape::API` to `Grape::API::Instance`.
|
407
|
+
|
408
|
+
Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
|
409
|
+
|
410
|
+
**Deprecated**
|
411
|
+
```ruby
|
412
|
+
class Grape::API
|
413
|
+
# your patched logic
|
414
|
+
...
|
415
|
+
end
|
416
|
+
```
|
417
|
+
**New**
|
418
|
+
```ruby
|
419
|
+
class Grape::API::Instance
|
420
|
+
# your patched logic
|
421
|
+
...
|
422
|
+
end
|
423
|
+
```
|
424
|
+
|
425
|
+
##### `name` (and other caveats) of the mounted API
|
426
|
+
|
427
|
+
After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
|
428
|
+
which inherit from `Grape::API::Instance`.
|
429
|
+
|
430
|
+
What this means in practice, is:
|
431
|
+
|
432
|
+
- Generally: you can access the named class from the instance calling the getter `base`.
|
433
|
+
- In particular: If you need the `name`, you can use `base`.`name`.
|
434
|
+
|
435
|
+
**Deprecated**
|
436
|
+
|
437
|
+
```ruby
|
438
|
+
payload[:endpoint].options[:for].name
|
439
|
+
```
|
440
|
+
|
441
|
+
**New**
|
442
|
+
|
443
|
+
```ruby
|
444
|
+
payload[:endpoint].options[:for].base.name
|
445
|
+
```
|
446
|
+
|
447
|
+
#### Changes in rescue_from returned object
|
448
|
+
|
449
|
+
Grape will now check the object returned from `rescue_from` and ensure that it is a `Rack::Response`. That makes sure response is valid and avoids exposing service information. Change any code that invoked `Rack::Response.new(...).finish` in a custom `rescue_from` block to `Rack::Response.new(...)` to comply with the validation.
|
450
|
+
|
451
|
+
```ruby
|
452
|
+
class Twitter::API < Grape::API
|
453
|
+
rescue_from :all do |e|
|
454
|
+
# version prior to 1.2.0
|
455
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
|
456
|
+
# 1.2.0 version
|
457
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
|
458
|
+
end
|
459
|
+
end
|
460
|
+
```
|
461
|
+
|
462
|
+
See [#1757](https://github.com/ruby-grape/grape/pull/1757) and [#1776](https://github.com/ruby-grape/grape/pull/1776) for more information.
|
463
|
+
|
464
|
+
### Upgrading to >= 1.1.0
|
465
|
+
|
466
|
+
#### Changes in HTTP Response Code for Unsupported Content Type
|
467
|
+
|
468
|
+
For PUT, POST, PATCH, and DELETE requests where a non-empty body and a "Content-Type" header is supplied that is not supported by the Grape API, Grape will no longer return a 406 "Not Acceptable" HTTP status code and will instead return a 415 "Unsupported Media Type" so that the usage of HTTP status code falls more in line with the specification of [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt).
|
469
|
+
|
4
470
|
### Upgrading to >= 1.0.0
|
5
471
|
|
6
472
|
#### Changes in XML and JSON Parsers
|
@@ -64,8 +530,7 @@ See [#1610](https://github.com/ruby-grape/grape/pull/1610) for more information.
|
|
64
530
|
|
65
531
|
#### The `except`, `except_message`, and `proc` options of the `values` validator are deprecated.
|
66
532
|
|
67
|
-
The new `except_values` validator should be used in place of the `except` and `except_message` options of
|
68
|
-
the `values` validator.
|
533
|
+
The new `except_values` validator should be used in place of the `except` and `except_message` options of the `values` validator.
|
69
534
|
|
70
535
|
Arity one Procs may now be used directly as the `values` option to explicitly test param values.
|
71
536
|
|
@@ -141,9 +606,7 @@ get '/example' #=> before: 405, after: 404
|
|
141
606
|
|
142
607
|
#### Removed param processing from built-in OPTIONS handler
|
143
608
|
|
144
|
-
When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
|
145
|
-
callbacks associated with the resource will be run. The `before_validation` and
|
146
|
-
`after_validation` callbacks and parameter validations will be skipped.
|
609
|
+
When a request is made to the built-in `OPTIONS` handler, only the `before` and `after` callbacks associated with the resource will be run. The `before_validation` and `after_validation` callbacks and parameter validations will be skipped.
|
147
610
|
|
148
611
|
See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
|
149
612
|
|
@@ -164,8 +627,7 @@ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
|
|
164
627
|
|
165
628
|
#### The default status code for DELETE is now 204 instead of 200.
|
166
629
|
|
167
|
-
Breaking change: Sets the default response status code for a delete request to 204.
|
168
|
-
A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
|
630
|
+
Breaking change: Sets the default response status code for a delete request to 204. A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
|
169
631
|
|
170
632
|
To achieve the old behavior, one has to set it explicitly:
|
171
633
|
```ruby
|
@@ -343,18 +805,14 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
|
|
343
805
|
|
344
806
|
#### Bypasses formatters when status code indicates no content
|
345
807
|
|
346
|
-
To be consistent with rack and it's handling of standard responses
|
347
|
-
associated with no content, both default and custom formatters will now
|
808
|
+
To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
|
348
809
|
be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
|
349
810
|
|
350
811
|
See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
|
351
812
|
|
352
813
|
#### Redirects respond as plain text with message
|
353
814
|
|
354
|
-
`#redirect` now uses `text/plain` regardless of whether that format has
|
355
|
-
been enabled. This prevents formatters from attempting to serialize the
|
356
|
-
message body and allows for a descriptive message body to be provided - and
|
357
|
-
optionally overridden - that better fulfills the theme of the HTTP spec.
|
815
|
+
`#redirect` now uses `text/plain` regardless of whether that format has been enabled. This prevents formatters from attempting to serialize the message body and allows for a descriptive message body to be provided - and optionally overridden - that better fulfills the theme of the HTTP spec.
|
358
816
|
|
359
817
|
See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
|
360
818
|
|
@@ -388,10 +846,7 @@ end
|
|
388
846
|
|
389
847
|
See [#1029](https://github.com/ruby-grape/grape/pull/1029) for more information.
|
390
848
|
|
391
|
-
There is a known issue because of this change. When Grape is used with an older
|
392
|
-
than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised
|
393
|
-
the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's
|
394
|
-
lines as last ones in the backtrace:
|
849
|
+
There is a known issue because of this change. When Grape is used with an older than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's lines as last ones in the backtrace:
|
395
850
|
|
396
851
|
```
|
397
852
|
NoMethodError: undefined method `[]' for nil:NilClass
|
data/grape.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
2
4
|
require 'grape/version'
|
3
5
|
|
4
6
|
Gem::Specification.new do |s|
|
@@ -11,15 +13,24 @@ Gem::Specification.new do |s|
|
|
11
13
|
s.summary = 'A simple Ruby framework for building REST-like APIs.'
|
12
14
|
s.description = 'A Ruby framework for rapid API development with great conventions.'
|
13
15
|
s.license = 'MIT'
|
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",
|
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}"
|
21
|
+
}
|
14
22
|
|
15
|
-
s.add_runtime_dependency 'rack', '>= 1.3.0'
|
16
|
-
s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
|
17
|
-
s.add_runtime_dependency 'rack-accept'
|
18
23
|
s.add_runtime_dependency 'activesupport'
|
19
|
-
s.add_runtime_dependency 'virtus', '>= 1.0.0'
|
20
24
|
s.add_runtime_dependency 'builder'
|
25
|
+
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'
|
21
29
|
|
22
|
-
s.files =
|
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/**/*']
|
23
33
|
s.test_files = Dir['spec/**/*']
|
24
34
|
s.require_paths = ['lib']
|
35
|
+
s.required_ruby_version = '>= 2.5.0'
|
25
36
|
end
|