grape 1.8.0 → 3.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +391 -1
- data/CONTRIBUTING.md +3 -11
- data/README.md +648 -552
- data/UPGRADING.md +803 -7
- data/grape.gemspec +11 -14
- data/lib/grape/api/instance.rb +68 -158
- data/lib/grape/api.rb +71 -107
- data/lib/grape/content_types.rb +23 -11
- data/lib/grape/cookies.rb +28 -25
- data/lib/grape/declared_params_handler.rb +116 -0
- data/lib/grape/dry_types.rb +48 -6
- data/lib/grape/dsl/callbacks.rb +14 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +15 -63
- data/lib/grape/dsl/entity.rb +88 -0
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +83 -64
- data/lib/grape/dsl/inside_route.rb +56 -295
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +50 -82
- data/lib/grape/dsl/request_response.rb +144 -146
- data/lib/grape/dsl/rescue_options.rb +24 -0
- data/lib/grape/dsl/routing.rb +261 -186
- data/lib/grape/dsl/settings.rb +29 -135
- data/lib/grape/dsl/validations.rb +39 -32
- data/lib/grape/dsl/version_options.rb +23 -0
- data/lib/grape/endpoint/options.rb +19 -0
- data/lib/grape/endpoint.rb +277 -250
- data/lib/grape/{util/env.rb → env.rb} +1 -8
- data/lib/grape/error_formatter/base.rb +56 -21
- data/lib/grape/error_formatter/json.rb +13 -15
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +12 -18
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +5 -25
- data/lib/grape/exceptions/base.rb +22 -58
- data/lib/grape/exceptions/error_response.rb +45 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/internal_server_error.rb +16 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/request_error.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/exceptions/validation.rb +25 -9
- data/lib/grape/exceptions/validation_array_errors.rb +5 -0
- data/lib/grape/exceptions/validation_errors.rb +22 -26
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +6 -10
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/json.rb +46 -0
- data/lib/grape/locale/en.yml +43 -42
- data/lib/grape/middleware/auth/base.rb +11 -33
- data/lib/grape/middleware/auth/dsl.rb +37 -31
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +56 -33
- data/lib/grape/middleware/deprecated_options_hash_access.rb +19 -0
- data/lib/grape/middleware/error.rb +183 -91
- data/lib/grape/middleware/formatter.rb +94 -105
- data/lib/grape/middleware/precomputed_content_types.rb +46 -0
- data/lib/grape/middleware/stack.rb +27 -38
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +72 -0
- data/lib/grape/middleware/versioner/header.rb +44 -129
- data/lib/grape/middleware/versioner/param.rb +4 -25
- data/lib/grape/middleware/versioner/path.rb +13 -32
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +18 -12
- data/lib/grape/params_builder/base.rb +20 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +15 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +38 -63
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +168 -26
- data/lib/grape/router/base_route.rb +48 -0
- data/lib/grape/router/greedy_route.rb +26 -0
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern.rb +30 -34
- data/lib/grape/router/route.rb +28 -61
- data/lib/grape/router.rb +71 -86
- data/lib/grape/serve_stream/file_body.rb +1 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +1 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +58 -0
- data/lib/grape/util/base_inheritable.rb +14 -14
- data/lib/grape/util/cache.rb +1 -3
- data/lib/grape/util/deep_freeze.rb +35 -0
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +51 -27
- data/lib/grape/util/inheritable_values.rb +7 -5
- data/lib/grape/util/lazy/base.rb +16 -0
- data/lib/grape/util/lazy/block.rb +22 -0
- data/lib/grape/util/lazy/value.rb +31 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +31 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +67 -0
- data/lib/grape/util/path_normalizer.rb +34 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +12 -14
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +43 -27
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +28 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +39 -0
- data/lib/grape/validations/param_scope_tracker.rb +62 -0
- data/lib/grape/validations/params_documentation.rb +52 -0
- data/lib/grape/validations/params_scope.rb +180 -303
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +2 -2
- data/lib/grape/validations/types/array_coercer.rb +4 -7
- data/lib/grape/validations/types/custom_type_coercer.rb +41 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +15 -28
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +11 -35
- data/lib/grape/validations/types/set_coercer.rb +1 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +11 -0
- data/lib/grape/validations/types.rb +29 -54
- data/lib/grape/validations/validations_spec.rb +164 -0
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
- data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
- data/lib/grape/validations/validators/base.rb +120 -36
- data/lib/grape/validations/validators/coerce_validator.rb +18 -36
- data/lib/grape/validations/validators/contract_scope_validator.rb +45 -0
- data/lib/grape/validations/validators/default_validator.rb +13 -16
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
- data/lib/grape/validations/validators/except_values_validator.rb +15 -5
- data/lib/grape/validations/validators/length_validator.rb +48 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
- data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
- data/lib/grape/validations/validators/oneof_validator.rb +49 -0
- data/lib/grape/validations/validators/presence_validator.rb +4 -2
- data/lib/grape/validations/validators/regexp_validator.rb +11 -3
- data/lib/grape/validations/validators/same_as_validator.rb +6 -15
- data/lib/grape/validations/validators/values_validator.rb +36 -65
- data/lib/grape/validations.rb +8 -21
- data/lib/grape/version.rb +1 -2
- data/lib/grape/xml.rb +17 -0
- data/lib/grape.rb +75 -288
- metadata +79 -291
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -19
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/exceptions/empty_message_body.rb +0 -11
- data/lib/grape/exceptions/missing_option.rb +0 -11
- data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
- data/lib/grape/exceptions/unknown_options.rb +0 -11
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -27
- data/lib/grape/extensions/hash.rb +0 -22
- data/lib/grape/extensions/hashie/mash.rb +0 -26
- data/lib/grape/http/headers.rb +0 -61
- data/lib/grape/middleware/globals.rb +0 -16
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/json.rb +0 -12
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/util/xml.rb +0 -10
- data/lib/grape/validations/attributes_doc.rb +0 -58
- data/lib/grape/validations/types/build_coercer.rb +0 -94
- data/lib/grape/validations/validator_factory.rb +0 -15
- data/spec/grape/api/custom_validations_spec.rb +0 -213
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -509
- data/spec/grape/api_spec.rb +0 -4356
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -98
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -531
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -225
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/grape_spec.rb +0 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -126
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/base_spec.rb +0 -38
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -733
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2030
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/deprecated_class_examples.rb +0 -16
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
|
@@ -5,10 +5,12 @@ module Grape
|
|
|
5
5
|
# Class to handle the stack of middlewares based on ActionDispatch::MiddlewareStack
|
|
6
6
|
# It allows to insert and insert after
|
|
7
7
|
class Stack
|
|
8
|
+
extend Forwardable
|
|
9
|
+
|
|
8
10
|
class Middleware
|
|
9
11
|
attr_reader :args, :block, :klass
|
|
10
12
|
|
|
11
|
-
def initialize(klass,
|
|
13
|
+
def initialize(klass, args, block)
|
|
12
14
|
@klass = klass
|
|
13
15
|
@args = args
|
|
14
16
|
@block = block
|
|
@@ -26,13 +28,18 @@ module Grape
|
|
|
26
28
|
klass == other || (name.nil? && klass.superclass == other)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
31
|
+
alias eql? ==
|
|
29
32
|
|
|
30
33
|
def inspect
|
|
31
34
|
klass.to_s
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
def
|
|
35
|
-
|
|
37
|
+
def build(builder)
|
|
38
|
+
# we need to force the ruby2_keywords_hash for middlewares that initialize contains keywords
|
|
39
|
+
# like ActionDispatch::RequestId since middleware arguments are serialized
|
|
40
|
+
# https://rubyapi.org/3.4/o/hash#method-c-ruby2_keywords_hash
|
|
41
|
+
args[-1] = Hash.ruby2_keywords_hash(args[-1]) if args.last.is_a?(Hash) && Hash.respond_to?(:ruby2_keywords_hash)
|
|
42
|
+
builder.use(klass, *args, &block)
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|
|
@@ -40,73 +47,55 @@ module Grape
|
|
|
40
47
|
|
|
41
48
|
attr_accessor :middlewares, :others
|
|
42
49
|
|
|
50
|
+
def_delegators :middlewares, :each, :size, :last, :[]
|
|
51
|
+
|
|
43
52
|
def initialize
|
|
44
53
|
@middlewares = []
|
|
45
54
|
@others = []
|
|
46
55
|
end
|
|
47
56
|
|
|
48
|
-
def
|
|
49
|
-
@middlewares.each(&block)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def size
|
|
53
|
-
middlewares.size
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def last
|
|
57
|
-
middlewares.last
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def [](i)
|
|
61
|
-
middlewares[i]
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def insert(index, *args, &block)
|
|
57
|
+
def insert(index, klass, *args, &block)
|
|
65
58
|
index = assert_index(index, :before)
|
|
66
|
-
|
|
67
|
-
middlewares.insert(index, middleware)
|
|
59
|
+
middlewares.insert(index, self.class::Middleware.new(klass, args, block))
|
|
68
60
|
end
|
|
69
|
-
ruby2_keywords :insert if respond_to?(:ruby2_keywords, true)
|
|
70
61
|
|
|
71
62
|
alias insert_before insert
|
|
72
63
|
|
|
73
|
-
def insert_after(index,
|
|
64
|
+
def insert_after(index, ...)
|
|
74
65
|
index = assert_index(index, :after)
|
|
75
|
-
insert(index + 1,
|
|
66
|
+
insert(index + 1, ...)
|
|
76
67
|
end
|
|
77
|
-
ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
|
|
78
68
|
|
|
79
|
-
def use(*args, &block)
|
|
80
|
-
middleware = self.class::Middleware.new(
|
|
69
|
+
def use(klass, *args, &block)
|
|
70
|
+
middleware = self.class::Middleware.new(klass, args, block)
|
|
81
71
|
middlewares.push(middleware)
|
|
82
72
|
end
|
|
83
|
-
ruby2_keywords :use if respond_to?(:ruby2_keywords, true)
|
|
84
73
|
|
|
85
74
|
def merge_with(middleware_specs)
|
|
86
|
-
middleware_specs.each do |operation, *args|
|
|
75
|
+
middleware_specs.each do |operation, klass, *args|
|
|
87
76
|
if args.last.is_a?(Proc)
|
|
88
77
|
last_proc = args.pop
|
|
89
|
-
public_send(operation, *args, &last_proc)
|
|
78
|
+
public_send(operation, klass, *args, &last_proc)
|
|
90
79
|
else
|
|
91
|
-
public_send(operation, *args)
|
|
80
|
+
public_send(operation, klass, *args)
|
|
92
81
|
end
|
|
93
82
|
end
|
|
94
83
|
end
|
|
95
84
|
|
|
96
85
|
# @return [Rack::Builder] the builder object with our middlewares applied
|
|
97
|
-
def build
|
|
86
|
+
def build
|
|
87
|
+
builder = Rack::Builder.new
|
|
98
88
|
others.shift(others.size).each { |m| merge_with(m) }
|
|
99
|
-
middlewares.each
|
|
100
|
-
m.use_in(builder)
|
|
101
|
-
end
|
|
89
|
+
middlewares.each { |m| m.build(builder) }
|
|
102
90
|
builder
|
|
103
91
|
end
|
|
104
92
|
|
|
105
93
|
# @description Add middlewares with :use operation to the stack. Store others with :insert_* operation for later
|
|
106
94
|
# @param [Array] other_specs An array of middleware specifications (e.g. [[:use, klass], [:insert_before, *args]])
|
|
107
95
|
def concat(other_specs)
|
|
108
|
-
|
|
109
|
-
|
|
96
|
+
use, not_use = other_specs.partition { |o| o.first == :use }
|
|
97
|
+
others << not_use
|
|
98
|
+
merge_with(use)
|
|
110
99
|
end
|
|
111
100
|
|
|
112
101
|
protected
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
module Versioner
|
|
@@ -20,44 +18,19 @@ module Grape
|
|
|
20
18
|
# route.
|
|
21
19
|
class AcceptVersionHeader < Base
|
|
22
20
|
def before
|
|
23
|
-
potential_version = (env[
|
|
24
|
-
|
|
25
|
-
if strict? && potential_version.empty?
|
|
26
|
-
# If no Accept-Version header:
|
|
27
|
-
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
return if potential_version.empty?
|
|
21
|
+
potential_version = try_scrub(env['HTTP_ACCEPT_VERSION'])
|
|
22
|
+
not_acceptable!('Accept-Version header must be set.') if strict && potential_version.blank?
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
throw :error, status: 406, headers: error_headers, message: 'The requested version is not supported.' unless versions.any? { |v| v.to_s == potential_version }
|
|
24
|
+
return if potential_version.blank?
|
|
34
25
|
|
|
26
|
+
not_acceptable!('The requested version is not supported.') unless potential_version_match?(potential_version)
|
|
35
27
|
env[Grape::Env::API_VERSION] = potential_version
|
|
36
28
|
end
|
|
37
29
|
|
|
38
30
|
private
|
|
39
31
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def strict?
|
|
45
|
-
options[:version_options] && options[:version_options][:strict]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
|
|
49
|
-
# of routes (see Grape::Router) for more information). To prevent
|
|
50
|
-
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
|
51
|
-
def cascade?
|
|
52
|
-
if options[:version_options]&.key?(:cascade)
|
|
53
|
-
options[:version_options][:cascade]
|
|
54
|
-
else
|
|
55
|
-
true
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def error_headers
|
|
60
|
-
cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
|
|
32
|
+
def not_acceptable!(message)
|
|
33
|
+
throw :error, Grape::Exceptions::ErrorResponse.new(status: 406, headers: error_headers, message:)
|
|
61
34
|
end
|
|
62
35
|
end
|
|
63
36
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Middleware
|
|
5
|
+
module Versioner
|
|
6
|
+
class Base < Grape::Middleware::Base
|
|
7
|
+
extend Forwardable
|
|
8
|
+
include Grape::Middleware::PrecomputedContentTypes
|
|
9
|
+
|
|
10
|
+
Options = Data.define(
|
|
11
|
+
:content_types, :format, :mount_path, :pattern, :prefix, :version_options, :versions
|
|
12
|
+
) do
|
|
13
|
+
include Grape::Middleware::DeprecatedOptionsHashAccess
|
|
14
|
+
|
|
15
|
+
def initialize(
|
|
16
|
+
content_types: nil, format: nil, mount_path: nil, pattern: /.*/i, prefix: nil,
|
|
17
|
+
version_options: Grape::DSL::VersionOptions.new, versions: nil
|
|
18
|
+
)
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @deprecated Kept as a frozen Hash representation of the {Options}
|
|
24
|
+
# defaults for back-compat. Will be removed in a future release.
|
|
25
|
+
DEFAULT_OPTIONS = Options.new.to_h.freeze
|
|
26
|
+
|
|
27
|
+
CASCADE_PASS_HEADER = { 'X-Cascade' => 'pass' }.freeze
|
|
28
|
+
|
|
29
|
+
def self.inherited(klass)
|
|
30
|
+
super
|
|
31
|
+
Versioner.register(klass)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_reader :available_media_types, :error_headers, :versions
|
|
35
|
+
|
|
36
|
+
def_delegators :config, :mount_path, :pattern, :prefix, :version_options
|
|
37
|
+
def_delegators :version_options, :cascade, :parameter, :strict, :vendor
|
|
38
|
+
|
|
39
|
+
def initialize(app, **options)
|
|
40
|
+
super
|
|
41
|
+
@versions = config.versions&.map(&:to_s) # making sure versions are strings to ease potential match
|
|
42
|
+
@error_headers = cascade ? CASCADE_PASS_HEADER : {}
|
|
43
|
+
@available_media_types = build_available_media_types
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def potential_version_match?(potential_version)
|
|
47
|
+
versions.blank? || versions.include?(potential_version)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def version_not_found!
|
|
51
|
+
throw :error, Grape::Exceptions::ErrorResponse.new(status: 404, message: '404 API Version Not Found', headers: CASCADE_PASS_HEADER)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def build_available_media_types
|
|
57
|
+
media_types = []
|
|
58
|
+
base_media_type = "application/vnd.#{vendor}"
|
|
59
|
+
versions&.reverse_each do |version|
|
|
60
|
+
versioned_base = "#{base_media_type}-#{version}"
|
|
61
|
+
content_types.each_key { |extension| media_types << "#{versioned_base}+#{extension}" }
|
|
62
|
+
media_types << versioned_base
|
|
63
|
+
end
|
|
64
|
+
content_types.each_key { |extension| media_types << "#{base_media_type}+#{extension}" }
|
|
65
|
+
media_types << base_media_type
|
|
66
|
+
media_types.concat(content_types.values.flatten)
|
|
67
|
+
media_types
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
require 'grape/middleware/versioner/parse_media_type_patch'
|
|
5
|
-
|
|
6
3
|
module Grape
|
|
7
4
|
module Middleware
|
|
8
5
|
module Versioner
|
|
@@ -25,168 +22,86 @@ module Grape
|
|
|
25
22
|
# X-Cascade header to alert Grape::Router to attempt the next matched
|
|
26
23
|
# route.
|
|
27
24
|
class Header < Base
|
|
28
|
-
VENDOR_VERSION_HEADER_REGEX =
|
|
29
|
-
/\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
|
|
30
|
-
|
|
31
|
-
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#{Regexp.last_match(0)}\^]+/.freeze
|
|
32
|
-
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))+/.freeze
|
|
33
|
-
|
|
34
25
|
def before
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
match_best_quality_media_type! do |media_type|
|
|
27
|
+
env.update(
|
|
28
|
+
Grape::Env::API_TYPE => media_type.type,
|
|
29
|
+
Grape::Env::API_SUBTYPE => media_type.subtype,
|
|
30
|
+
Grape::Env::API_VENDOR => media_type.vendor,
|
|
31
|
+
Grape::Env::API_VERSION => media_type.version,
|
|
32
|
+
Grape::Env::API_FORMAT => media_type.format
|
|
33
|
+
)
|
|
43
34
|
end
|
|
44
35
|
end
|
|
45
36
|
|
|
46
37
|
private
|
|
47
38
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
strict_version_vendor_accept_header_presence_check
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def strict_accept_header_presence_check
|
|
54
|
-
return unless header.qvalues.empty?
|
|
55
|
-
|
|
56
|
-
fail_with_invalid_accept_header!('Accept header must be set.')
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def strict_version_vendor_accept_header_presence_check
|
|
60
|
-
return if versions.blank? || an_accept_header_with_version_and_vendor_is_present?
|
|
61
|
-
|
|
62
|
-
fail_with_invalid_accept_header!('API vendor or version not found.')
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def an_accept_header_with_version_and_vendor_is_present?
|
|
66
|
-
header.qvalues.keys.any? do |h|
|
|
67
|
-
VENDOR_VERSION_HEADER_REGEX.match?(h.sub('application/', ''))
|
|
68
|
-
end
|
|
69
|
-
end
|
|
39
|
+
def match_best_quality_media_type!
|
|
40
|
+
return unless vendor
|
|
70
41
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
42
|
+
strict_header_checks!
|
|
43
|
+
media_type = Grape::Util::MediaType.best_quality(accept_header, available_media_types)
|
|
44
|
+
return yield media_type if media_type
|
|
74
45
|
|
|
75
|
-
|
|
76
|
-
@media_type ||= header.best_of(available_media_types)
|
|
46
|
+
fail!
|
|
77
47
|
end
|
|
78
48
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
env[Grape::Env::API_TYPE] = type
|
|
82
|
-
env[Grape::Env::API_SUBTYPE] = subtype
|
|
83
|
-
|
|
84
|
-
return unless VENDOR_VERSION_HEADER_REGEX =~ subtype
|
|
85
|
-
|
|
86
|
-
env[Grape::Env::API_VENDOR] = Regexp.last_match[1]
|
|
87
|
-
env[Grape::Env::API_VERSION] = Regexp.last_match[2]
|
|
88
|
-
# weird that Grape::Middleware::Formatter also does this
|
|
89
|
-
env[Grape::Env::API_FORMAT] = Regexp.last_match[3]
|
|
49
|
+
def accept_header
|
|
50
|
+
env['HTTP_ACCEPT']
|
|
90
51
|
end
|
|
91
52
|
|
|
92
|
-
def
|
|
93
|
-
|
|
94
|
-
.new(message, error_headers)
|
|
95
|
-
end
|
|
53
|
+
def strict_header_checks!
|
|
54
|
+
return unless strict
|
|
96
55
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.new(message, error_headers)
|
|
56
|
+
accept_header_check!
|
|
57
|
+
version_and_vendor_check!
|
|
100
58
|
end
|
|
101
59
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
content_types.each_key do |extension|
|
|
105
|
-
versions.reverse_each do |version|
|
|
106
|
-
available_media_types << "application/vnd.#{vendor}-#{version}+#{extension}"
|
|
107
|
-
available_media_types << "application/vnd.#{vendor}-#{version}"
|
|
108
|
-
end
|
|
109
|
-
available_media_types << "application/vnd.#{vendor}+#{extension}"
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
available_media_types << "application/vnd.#{vendor}"
|
|
113
|
-
available_media_types.concat(content_types.values.flatten)
|
|
114
|
-
end
|
|
115
|
-
end
|
|
60
|
+
def accept_header_check!
|
|
61
|
+
return if accept_header.present?
|
|
116
62
|
|
|
117
|
-
|
|
118
|
-
header.values.all? do |header_value|
|
|
119
|
-
vendor?(header_value) && request_vendor(header_value) != vendor
|
|
120
|
-
end
|
|
63
|
+
invalid_accept_header!('Accept header must be set.')
|
|
121
64
|
end
|
|
122
65
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
version?(header_value) && versions.exclude?(request_version(header_value))
|
|
126
|
-
end
|
|
127
|
-
end
|
|
66
|
+
def version_and_vendor_check!
|
|
67
|
+
return if versions.blank? || version_and_vendor?
|
|
128
68
|
|
|
129
|
-
|
|
130
|
-
Rack::Accept::MediaType.new env[Grape::Http::Headers::HTTP_ACCEPT]
|
|
131
|
-
rescue RuntimeError => e
|
|
132
|
-
fail_with_invalid_accept_header!(e.message)
|
|
69
|
+
invalid_accept_header!('API vendor or version not found.')
|
|
133
70
|
end
|
|
134
71
|
|
|
135
|
-
def
|
|
136
|
-
|
|
72
|
+
def q_values_mime_types
|
|
73
|
+
@q_values_mime_types ||= Rack::Utils.q_values(accept_header).map(&:first)
|
|
137
74
|
end
|
|
138
75
|
|
|
139
|
-
def
|
|
140
|
-
|
|
76
|
+
def version_and_vendor?
|
|
77
|
+
q_values_mime_types.any? { |mime_type| Grape::Util::MediaType.match?(mime_type) }
|
|
141
78
|
end
|
|
142
79
|
|
|
143
|
-
def
|
|
144
|
-
|
|
80
|
+
def invalid_accept_header!(message)
|
|
81
|
+
raise Grape::Exceptions::InvalidAcceptHeader.new(message, error_headers)
|
|
145
82
|
end
|
|
146
83
|
|
|
147
|
-
def
|
|
148
|
-
|
|
84
|
+
def invalid_version_header!(message)
|
|
85
|
+
raise Grape::Exceptions::InvalidVersionHeader.new(message, error_headers)
|
|
149
86
|
end
|
|
150
87
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
# (see Grape::Router for more
|
|
154
|
-
# information). To prevent # this behavior, and not add the `X-Cascade`
|
|
155
|
-
# header, one can set the `:cascade` option to `false`.
|
|
156
|
-
def cascade?
|
|
157
|
-
if version_options&.key?(:cascade)
|
|
158
|
-
version_options[:cascade]
|
|
159
|
-
else
|
|
160
|
-
true
|
|
161
|
-
end
|
|
162
|
-
end
|
|
88
|
+
def fail!
|
|
89
|
+
return if env[Grape::Env::GRAPE_ALLOWED_METHODS].present?
|
|
163
90
|
|
|
164
|
-
|
|
165
|
-
|
|
91
|
+
media_types = q_values_mime_types.map { |mime_type| Grape::Util::MediaType.parse(mime_type) }
|
|
92
|
+
vendor_not_found!(media_types) || version_not_found!(media_types)
|
|
166
93
|
end
|
|
167
94
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def vendor?(media_type)
|
|
171
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
172
|
-
subtype.present? && subtype[HAS_VENDOR_REGEX]
|
|
173
|
-
end
|
|
95
|
+
def vendor_not_found!(media_types)
|
|
96
|
+
return unless media_types.all? { |media_type| media_type&.vendor && media_type.vendor != vendor }
|
|
174
97
|
|
|
175
|
-
|
|
176
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
177
|
-
subtype.match(VENDOR_VERSION_HEADER_REGEX)[1]
|
|
98
|
+
invalid_accept_header!('API vendor not found.')
|
|
178
99
|
end
|
|
179
100
|
|
|
180
|
-
def
|
|
181
|
-
|
|
182
|
-
subtype.match(VENDOR_VERSION_HEADER_REGEX)[2]
|
|
183
|
-
end
|
|
101
|
+
def version_not_found!(media_types)
|
|
102
|
+
return unless media_types.all? { |media_type| media_type&.version && versions && !versions.include?(media_type.version) }
|
|
184
103
|
|
|
185
|
-
|
|
186
|
-
# @return [Boolean] whether the content type sets an API version
|
|
187
|
-
def version?(media_type)
|
|
188
|
-
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
|
189
|
-
subtype.present? && subtype[HAS_VERSION_REGEX]
|
|
104
|
+
invalid_version_header!('API version not found.')
|
|
190
105
|
end
|
|
191
106
|
end
|
|
192
107
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
module Versioner
|
|
@@ -21,31 +19,12 @@ module Grape
|
|
|
21
19
|
#
|
|
22
20
|
# env['api.version'] => 'v1'
|
|
23
21
|
class Param < Base
|
|
24
|
-
def default_options
|
|
25
|
-
{
|
|
26
|
-
version_options: {
|
|
27
|
-
parameter: 'apiver'
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
end
|
|
31
|
-
|
|
32
22
|
def before
|
|
33
|
-
potential_version =
|
|
34
|
-
return if potential_version.
|
|
35
|
-
|
|
36
|
-
throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' } if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
|
37
|
-
env[Grape::Env::API_VERSION] = potential_version
|
|
38
|
-
env[Grape::Env::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Grape::Env::RACK_REQUEST_QUERY_HASH
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
def paramkey
|
|
44
|
-
version_options[:parameter] || default_options[:version_options][:parameter]
|
|
45
|
-
end
|
|
23
|
+
potential_version = query_params[parameter]
|
|
24
|
+
return if potential_version.blank?
|
|
46
25
|
|
|
47
|
-
|
|
48
|
-
|
|
26
|
+
version_not_found! unless potential_version_match?(potential_version)
|
|
27
|
+
env[Grape::Env::API_VERSION] = env[Rack::RACK_REQUEST_QUERY_HASH].delete(parameter)
|
|
49
28
|
end
|
|
50
29
|
end
|
|
51
30
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/middleware/base'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Middleware
|
|
7
5
|
module Versioner
|
|
@@ -19,44 +17,27 @@ module Grape
|
|
|
19
17
|
# env['api.version'] => 'v1'
|
|
20
18
|
#
|
|
21
19
|
class Path < Base
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
20
|
+
def initialize(app, **options)
|
|
21
|
+
super
|
|
22
|
+
@prefixes = [mount_path, Grape::Util::PathNormalizer.call(prefix)].select { |p| p.present? && p != '/' }.freeze
|
|
26
23
|
end
|
|
27
24
|
|
|
28
25
|
def before
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
path_info = Grape::Util::PathNormalizer.call(env[Rack::PATH_INFO])
|
|
27
|
+
return if path_info == '/'
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
path.
|
|
34
|
-
path = Grape::Router.normalize_path(path)
|
|
29
|
+
path_info = @prefixes.reduce(path_info) do |pi, path|
|
|
30
|
+
pi.start_with?(path) ? pi.delete_prefix(path) : pi
|
|
35
31
|
end
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return unless potential_version&.match?(options[:pattern])
|
|
40
|
-
|
|
41
|
-
throw :error, status: 404, message: '404 API Version Not Found' if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
|
42
|
-
env[Grape::Env::API_VERSION] = potential_version
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
33
|
+
slash_position = path_info.index('/', 1) # omit the first one
|
|
34
|
+
return unless slash_position
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
return
|
|
36
|
+
potential_version = path_info[1..(slash_position - 1)]
|
|
37
|
+
return unless potential_version.match?(pattern)
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def mount_path
|
|
55
|
-
@mount_path ||= options[:mount_path] && options[:mount_path] != '/' ? options[:mount_path] : ''
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def prefix
|
|
59
|
-
Grape::Router.normalize_path(options[:prefix].to_s) if options[:prefix]
|
|
39
|
+
version_not_found! unless potential_version_match?(potential_version)
|
|
40
|
+
env[Grape::Env::API_VERSION] = potential_version
|
|
60
41
|
end
|
|
61
42
|
end
|
|
62
43
|
end
|
|
@@ -4,30 +4,23 @@
|
|
|
4
4
|
# on the requests. The current methods for determining version are:
|
|
5
5
|
#
|
|
6
6
|
# :header - version from HTTP Accept header.
|
|
7
|
+
# :accept_version_header - version from HTTP Accept-Version header
|
|
7
8
|
# :path - version from uri. e.g. /v1/resource
|
|
8
9
|
# :param - version from uri query string, e.g. /v1/resource?apiver=v1
|
|
9
|
-
#
|
|
10
10
|
# See individual classes for details.
|
|
11
11
|
module Grape
|
|
12
12
|
module Middleware
|
|
13
13
|
module Versioner
|
|
14
|
+
extend Grape::Util::Registry
|
|
15
|
+
|
|
14
16
|
module_function
|
|
15
17
|
|
|
16
|
-
# @param strategy [Symbol] :path, :header or :param
|
|
18
|
+
# @param strategy [Symbol] :path, :header, :accept_version_header or :param
|
|
17
19
|
# @return a middleware class based on strategy
|
|
18
20
|
def using(strategy)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
when :header
|
|
23
|
-
Header
|
|
24
|
-
when :param
|
|
25
|
-
Param
|
|
26
|
-
when :accept_version_header
|
|
27
|
-
AcceptVersionHeader
|
|
28
|
-
else
|
|
29
|
-
raise Grape::Exceptions::InvalidVersionerOption.new(strategy)
|
|
30
|
-
end
|
|
21
|
+
raise Grape::Exceptions::InvalidVersionerOption, strategy unless registry.key?(strategy)
|
|
22
|
+
|
|
23
|
+
registry[strategy]
|
|
31
24
|
end
|
|
32
25
|
end
|
|
33
26
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
# Marker module for a mountable Grape application. Both {Grape::API} (the
|
|
5
|
+
# remountable user-facing class) and {Grape::API::Instance} (the compiled
|
|
6
|
+
# engine) extend it, so a mounted Grape app can be told apart from a bare
|
|
7
|
+
# Rack app with `is_a?(Grape::Mountable)` — a single, explicit predicate
|
|
8
|
+
# rather than duck-typing on an incidental internal method such as
|
|
9
|
+
# `inheritable_setting`.
|
|
10
|
+
#
|
|
11
|
+
# `Grape::API` and `Grape::API::Instance` are not related by inheritance and
|
|
12
|
+
# do not even respond to the same methods (the former to `mount_instance`,
|
|
13
|
+
# the latter to `inheritable_setting`/`endpoints`), so there is no common
|
|
14
|
+
# ancestor to key an `is_a?` check on without this marker.
|
|
15
|
+
#
|
|
16
|
+
# It answers identity only ("is this a Grape app?"). Capability checks that
|
|
17
|
+
# go on to call a stage-specific method — e.g. `respond_to?(:endpoints)`
|
|
18
|
+
# before reading `endpoints` — must stay as they are, since a `Mountable`
|
|
19
|
+
# does not necessarily respond to every such method.
|
|
20
|
+
module Mountable
|
|
21
|
+
end
|
|
22
|
+
end
|