grape 1.1.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +370 -44
- data/CONTRIBUTING.md +32 -1
- data/LICENSE +1 -1
- data/README.md +683 -87
- data/UPGRADING.md +481 -17
- data/grape.gemspec +15 -4
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +279 -0
- data/lib/grape/api.rb +144 -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/dry_types.rb +12 -0
- data/lib/grape/dsl/api.rb +1 -1
- data/lib/grape/dsl/callbacks.rb +21 -1
- data/lib/grape/dsl/configuration.rb +1 -1
- data/lib/grape/dsl/desc.rb +41 -23
- data/lib/grape/dsl/headers.rb +7 -2
- data/lib/grape/dsl/helpers.rb +10 -7
- data/lib/grape/dsl/inside_route.rb +118 -62
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +11 -4
- data/lib/grape/dsl/parameters.rb +33 -19
- data/lib/grape/dsl/request_response.rb +12 -9
- data/lib/grape/dsl/routing.rb +22 -13
- data/lib/grape/dsl/settings.rb +10 -6
- data/lib/grape/dsl/validations.rb +19 -14
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +67 -58
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +11 -7
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +4 -6
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/exceptions/base.rb +23 -16
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
- data/lib/grape/exceptions/invalid_formatter.rb +2 -0
- data/lib/grape/exceptions/invalid_message_body.rb +2 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
- data/lib/grape/exceptions/method_not_allowed.rb +2 -0
- data/lib/grape/exceptions/missing_group_type.rb +10 -1
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +10 -1
- data/lib/grape/exceptions/validation.rb +5 -8
- 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 +11 -8
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +9 -2
- 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 +13 -8
- data/lib/grape/middleware/error.rb +22 -17
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +12 -10
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +16 -6
- 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 +14 -2
- 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 +21 -9
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +21 -17
- data/lib/grape/router/route.rb +15 -29
- data/lib/grape/router.rb +36 -29
- 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/types/invalid_value.rb +8 -0
- 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 +4 -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 +3 -1
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations/attributes_doc.rb +58 -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 +174 -94
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types/array_coercer.rb +63 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +72 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +17 -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 +75 -0
- data/lib/grape/validations/types/set_coercer.rb +38 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/types.rb +106 -63
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +84 -68
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/default_validator.rb +51 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +27 -16
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +88 -0
- data/lib/grape/validations.rb +18 -6
- data/lib/grape/version.rb +3 -1
- data/lib/grape.rb +175 -94
- data/spec/grape/api/custom_validations_spec.rb +117 -44
- data/spec/grape/api/deeply_included_options_spec.rb +4 -4
- data/spec/grape/api/defines_boolean_in_params_spec.rb +38 -0
- data/spec/grape/api/documentation_spec.rb +59 -0
- data/spec/grape/api/inherited_helpers_spec.rb +1 -1
- data/spec/grape/api/instance_spec.rb +103 -0
- data/spec/grape/api/invalid_format_spec.rb +3 -1
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/nested_helpers_spec.rb +1 -1
- data/spec/grape/api/optional_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/parameters_modification_spec.rb +2 -2
- data/spec/grape/api/patch_method_helpers_spec.rb +1 -1
- data/spec/grape/api/recognize_path_spec.rb +2 -2
- data/spec/grape/api/required_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +10 -16
- data/spec/grape/api/shared_helpers_spec.rb +1 -1
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +995 -231
- data/spec/grape/config_spec.rb +17 -0
- data/spec/grape/dsl/callbacks_spec.rb +3 -2
- data/spec/grape/dsl/desc_spec.rb +43 -17
- data/spec/grape/dsl/headers_spec.rb +40 -10
- data/spec/grape/dsl/helpers_spec.rb +6 -5
- data/spec/grape/dsl/inside_route_spec.rb +189 -38
- data/spec/grape/dsl/logger_spec.rb +17 -19
- data/spec/grape/dsl/middleware_spec.rb +11 -2
- data/spec/grape/dsl/parameters_spec.rb +3 -1
- data/spec/grape/dsl/request_response_spec.rb +8 -7
- data/spec/grape/dsl/routing_spec.rb +22 -9
- data/spec/grape/dsl/settings_spec.rb +1 -1
- data/spec/grape/dsl/validations_spec.rb +1 -16
- data/spec/grape/endpoint/declared_spec.rb +846 -0
- data/spec/grape/endpoint_spec.rb +136 -577
- data/spec/grape/entity_spec.rb +31 -24
- data/spec/grape/exceptions/base_spec.rb +81 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +4 -1
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +65 -23
- data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -2
- data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +1 -1
- data/spec/grape/exceptions/missing_option_spec.rb +2 -2
- data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +21 -15
- data/spec/grape/exceptions/validation_spec.rb +6 -4
- data/spec/grape/extensions/param_builders/hash_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +9 -9
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +9 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -10
- data/spec/grape/integration/rack_spec.rb +25 -8
- data/spec/grape/loading_spec.rb +9 -9
- data/spec/grape/middleware/auth/base_spec.rb +2 -1
- data/spec/grape/middleware/auth/dsl_spec.rb +19 -10
- data/spec/grape/middleware/auth/strategies_spec.rb +62 -22
- data/spec/grape/middleware/base_spec.rb +36 -17
- data/spec/grape/middleware/error_spec.rb +11 -4
- data/spec/grape/middleware/exception_spec.rb +112 -162
- data/spec/grape/middleware/formatter_spec.rb +65 -29
- data/spec/grape/middleware/globals_spec.rb +8 -5
- data/spec/grape/middleware/stack_spec.rb +25 -13
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -2
- data/spec/grape/middleware/versioner/header_spec.rb +37 -14
- data/spec/grape/middleware/versioner/param_spec.rb +8 -2
- data/spec/grape/middleware/versioner/path_spec.rb +6 -2
- data/spec/grape/middleware/versioner_spec.rb +2 -2
- data/spec/grape/named_api_spec.rb +19 -0
- data/spec/grape/parser_spec.rb +10 -6
- data/spec/grape/path_spec.rb +53 -53
- data/spec/grape/presenters/presenter_spec.rb +8 -7
- data/spec/grape/request_spec.rb +29 -3
- data/spec/grape/util/inheritable_setting_spec.rb +9 -8
- data/spec/grape/util/inheritable_values_spec.rb +5 -3
- data/spec/grape/util/reverse_stackable_values_spec.rb +5 -2
- data/spec/grape/util/stackable_values_spec.rb +10 -7
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -1
- data/spec/grape/validations/attributes_doc_spec.rb +153 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +13 -14
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +40 -0
- data/spec/grape/validations/params_scope_spec.rb +568 -99
- data/spec/grape/validations/single_attribute_iterator_spec.rb +57 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +33 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +150 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +32 -0
- data/spec/grape/validations/types_spec.rb +44 -45
- data/spec/grape/validations/validators/all_or_none_spec.rb +134 -32
- data/spec/grape/validations/validators/allow_blank_spec.rb +137 -141
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +169 -31
- data/spec/grape/validations/validators/coerce_spec.rb +491 -151
- data/spec/grape/validations/validators/default_spec.rb +242 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +198 -40
- data/spec/grape/validations/validators/except_values_spec.rb +6 -5
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +181 -30
- data/spec/grape/validations/validators/presence_spec.rb +45 -2
- data/spec/grape/validations/validators/regexp_spec.rb +27 -33
- data/spec/grape/validations/validators/same_as_spec.rb +57 -0
- data/spec/grape/validations/validators/values_spec.rb +227 -180
- data/spec/grape/validations_spec.rb +502 -72
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -2
- data/spec/integration/multi_xml/xml_spec.rb +2 -2
- data/spec/shared/versioning_examples.rb +34 -29
- data/spec/spec_helper.rb +31 -5
- data/spec/support/basic_auth_encode_helpers.rb +3 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +2 -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 +111 -61
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- 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/lib/grape/validations/validators/all_or_none.rb +0 -20
- data/lib/grape/validations/validators/allow_blank.rb +0 -16
- data/lib/grape/validations/validators/as.rb +0 -15
- data/lib/grape/validations/validators/at_least_one_of.rb +0 -20
- data/lib/grape/validations/validators/coerce.rb +0 -74
- data/lib/grape/validations/validators/default.rb +0 -48
- data/lib/grape/validations/validators/exactly_one_of.rb +0 -29
- data/lib/grape/validations/validators/except_values.rb +0 -20
- data/lib/grape/validations/validators/mutual_exclusion.rb +0 -25
- data/lib/grape/validations/validators/presence.rb +0 -10
- data/lib/grape/validations/validators/regexp.rb +0 -11
- data/lib/grape/validations/validators/values.rb +0 -71
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
- data/spec/grape/dsl/configuration_spec.rb +0 -14
- data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Middleware
|
3
5
|
# Class to handle the stack of middlewares based on ActionDispatch::MiddlewareStack
|
@@ -28,6 +30,10 @@ module Grape
|
|
28
30
|
def inspect
|
29
31
|
klass.to_s
|
30
32
|
end
|
33
|
+
|
34
|
+
def use_in(builder)
|
35
|
+
builder.use(@klass, *@args, &@block)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
include Enumerable
|
@@ -39,8 +45,8 @@ module Grape
|
|
39
45
|
@others = []
|
40
46
|
end
|
41
47
|
|
42
|
-
def each
|
43
|
-
@middlewares.each
|
48
|
+
def each(&block)
|
49
|
+
@middlewares.each(&block)
|
44
50
|
end
|
45
51
|
|
46
52
|
def size
|
@@ -60,6 +66,7 @@ module Grape
|
|
60
66
|
middleware = self.class::Middleware.new(*args, &block)
|
61
67
|
middlewares.insert(index, middleware)
|
62
68
|
end
|
69
|
+
ruby2_keywords :insert if respond_to?(:ruby2_keywords, true)
|
63
70
|
|
64
71
|
alias insert_before insert
|
65
72
|
|
@@ -67,16 +74,19 @@ module Grape
|
|
67
74
|
index = assert_index(index, :after)
|
68
75
|
insert(index + 1, *args, &block)
|
69
76
|
end
|
77
|
+
ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
|
70
78
|
|
71
79
|
def use(*args, &block)
|
72
80
|
middleware = self.class::Middleware.new(*args, &block)
|
73
81
|
middlewares.push(middleware)
|
74
82
|
end
|
83
|
+
ruby2_keywords :use if respond_to?(:ruby2_keywords, true)
|
75
84
|
|
76
85
|
def merge_with(middleware_specs)
|
77
86
|
middleware_specs.each do |operation, *args|
|
78
87
|
if args.last.is_a?(Proc)
|
79
|
-
|
88
|
+
last_proc = args.pop
|
89
|
+
public_send(operation, *args, &last_proc)
|
80
90
|
else
|
81
91
|
public_send(operation, *args)
|
82
92
|
end
|
@@ -85,9 +95,9 @@ module Grape
|
|
85
95
|
|
86
96
|
# @return [Rack::Builder] the builder object with our middlewares applied
|
87
97
|
def build(builder = Rack::Builder.new)
|
88
|
-
others.shift(others.size).each(
|
98
|
+
others.shift(others.size).each { |m| merge_with(m) }
|
89
99
|
middlewares.each do |m|
|
90
|
-
m.
|
100
|
+
m.use_in(builder)
|
91
101
|
end
|
92
102
|
builder
|
93
103
|
end
|
@@ -96,7 +106,7 @@ module Grape
|
|
96
106
|
# @param [Array] other_specs An array of middleware specifications (e.g. [[:use, klass], [:insert_before, *args]])
|
97
107
|
def concat(other_specs)
|
98
108
|
@others << Array(other_specs).reject { |o| o.first == :use }
|
99
|
-
merge_with
|
109
|
+
merge_with(Array(other_specs).select { |o| o.first == :use })
|
100
110
|
end
|
101
111
|
|
102
112
|
protected
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/middleware/base'
|
2
4
|
|
3
5
|
module Grape
|
@@ -20,11 +22,9 @@ module Grape
|
|
20
22
|
def before
|
21
23
|
potential_version = (env[Grape::Http::Headers::HTTP_ACCEPT_VERSION] || '').strip
|
22
24
|
|
23
|
-
if strict?
|
25
|
+
if strict? && potential_version.empty?
|
24
26
|
# If no Accept-Version header:
|
25
|
-
|
26
|
-
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
27
|
-
end
|
27
|
+
throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
|
28
28
|
end
|
29
29
|
|
30
30
|
return if potential_version.empty?
|
@@ -49,7 +49,7 @@ module Grape
|
|
49
49
|
# of routes (see Grape::Router) for more information). To prevent
|
50
50
|
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
51
51
|
def cascade?
|
52
|
-
if options[:version_options]
|
52
|
+
if options[:version_options]&.key?(:cascade)
|
53
53
|
options[:version_options][:cascade]
|
54
54
|
else
|
55
55
|
true
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/middleware/base'
|
2
4
|
require 'grape/middleware/versioner/parse_media_type_patch'
|
3
5
|
|
@@ -24,10 +26,10 @@ module Grape
|
|
24
26
|
# route.
|
25
27
|
class Header < Base
|
26
28
|
VENDOR_VERSION_HEADER_REGEX =
|
27
|
-
/\Avnd\.([a-z0-9.\-_
|
29
|
+
/\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
|
28
30
|
|
29
|
-
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_
|
30
|
-
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_
|
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
|
31
33
|
|
32
34
|
def before
|
33
35
|
strict_header_checks if strict?
|
@@ -50,18 +52,20 @@ module Grape
|
|
50
52
|
|
51
53
|
def strict_accept_header_presence_check
|
52
54
|
return unless header.qvalues.empty?
|
55
|
+
|
53
56
|
fail_with_invalid_accept_header!('Accept header must be set.')
|
54
57
|
end
|
55
58
|
|
56
59
|
def strict_version_vendor_accept_header_presence_check
|
57
60
|
return unless versions.present?
|
58
61
|
return if an_accept_header_with_version_and_vendor_is_present?
|
62
|
+
|
59
63
|
fail_with_invalid_accept_header!('API vendor or version not found.')
|
60
64
|
end
|
61
65
|
|
62
66
|
def an_accept_header_with_version_and_vendor_is_present?
|
63
67
|
header.qvalues.keys.any? do |h|
|
64
|
-
VENDOR_VERSION_HEADER_REGEX
|
68
|
+
VENDOR_VERSION_HEADER_REGEX.match?(h.sub('application/', ''))
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
@@ -99,7 +103,7 @@ module Grape
|
|
99
103
|
def available_media_types
|
100
104
|
available_media_types = []
|
101
105
|
|
102
|
-
content_types.
|
106
|
+
content_types.each_key do |extension|
|
103
107
|
versions.reverse_each do |version|
|
104
108
|
available_media_types += [
|
105
109
|
"application/vnd.#{vendor}-#{version}+#{extension}",
|
@@ -111,7 +115,7 @@ module Grape
|
|
111
115
|
|
112
116
|
available_media_types << "application/vnd.#{vendor}"
|
113
117
|
|
114
|
-
content_types.
|
118
|
+
content_types.each_value do |media_type|
|
115
119
|
available_media_types << media_type
|
116
120
|
end
|
117
121
|
|
@@ -158,7 +162,7 @@ module Grape
|
|
158
162
|
# information). To prevent # this behavior, and not add the `X-Cascade`
|
159
163
|
# header, one can set the `:cascade` option to `false`.
|
160
164
|
def cascade?
|
161
|
-
if version_options
|
165
|
+
if version_options&.key?(:cascade)
|
162
166
|
version_options[:cascade]
|
163
167
|
else
|
164
168
|
true
|
@@ -173,7 +177,7 @@ module Grape
|
|
173
177
|
# @return [Boolean] whether the content type sets a vendor
|
174
178
|
def vendor?(media_type)
|
175
179
|
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
176
|
-
subtype[HAS_VENDOR_REGEX]
|
180
|
+
subtype.present? && subtype[HAS_VENDOR_REGEX]
|
177
181
|
end
|
178
182
|
|
179
183
|
def request_vendor(media_type)
|
@@ -190,7 +194,7 @@ module Grape
|
|
190
194
|
# @return [Boolean] whether the content type sets an API version
|
191
195
|
def version?(media_type)
|
192
196
|
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
193
|
-
subtype[HAS_VERSION_REGEX]
|
197
|
+
subtype.present? && subtype[HAS_VERSION_REGEX]
|
194
198
|
end
|
195
199
|
end
|
196
200
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/middleware/base'
|
2
4
|
|
3
5
|
module Grape
|
@@ -22,7 +24,7 @@ module Grape
|
|
22
24
|
def default_options
|
23
25
|
{
|
24
26
|
version_options: {
|
25
|
-
parameter: 'apiver'
|
27
|
+
parameter: 'apiver'
|
26
28
|
}
|
27
29
|
}
|
28
30
|
end
|
@@ -30,6 +32,7 @@ module Grape
|
|
30
32
|
def before
|
31
33
|
potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
|
32
34
|
return if potential_version.nil?
|
35
|
+
|
33
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 }
|
34
37
|
env[Grape::Env::API_VERSION] = potential_version
|
35
38
|
env[Grape::Env::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Grape::Env::RACK_REQUEST_QUERY_HASH
|
@@ -1,11 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
1
4
|
module Rack
|
2
5
|
module Accept
|
3
6
|
module Header
|
7
|
+
ALLOWED_CHARACTERS = %r{^([a-z*]+)/([a-z0-9*&\^\-_#{$ERROR_INFO}.+]+)(?:;([a-z0-9=;]+))?$}.freeze
|
4
8
|
class << self
|
5
9
|
# Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
|
6
10
|
def parse_media_type(media_type)
|
7
11
|
# see http://tools.ietf.org/html/rfc6838#section-4.2 for allowed characters in media type names
|
8
|
-
m = media_type
|
12
|
+
m = media_type&.match(ALLOWED_CHARACTERS)
|
9
13
|
m ? [m[1], m[2], m[3] || ''] : []
|
10
14
|
end
|
11
15
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'grape/middleware/base'
|
2
4
|
|
3
5
|
module Grape
|
@@ -34,7 +36,8 @@ module Grape
|
|
34
36
|
|
35
37
|
pieces = path.split('/')
|
36
38
|
potential_version = pieces[1]
|
37
|
-
return unless potential_version
|
39
|
+
return unless potential_version&.match?(options[:pattern])
|
40
|
+
|
38
41
|
throw :error, status: 404, message: '404 API Version Not Found' if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
39
42
|
env[Grape::Env::API_VERSION] = potential_version
|
40
43
|
end
|
@@ -43,6 +46,7 @@ module Grape
|
|
43
46
|
|
44
47
|
def mounted_path?(path)
|
45
48
|
return false unless mount_path && path.start_with?(mount_path)
|
49
|
+
|
46
50
|
rest = path.slice(mount_path.length..-1)
|
47
51
|
rest.start_with?('/') || rest.empty?
|
48
52
|
end
|
data/lib/grape/namespace.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'grape/util/cache'
|
4
|
+
|
1
5
|
module Grape
|
2
6
|
# A container for endpoints or other namespaces, which allows for both
|
3
7
|
# logical grouping of endpoints as well as sharing common configuration.
|
@@ -23,13 +27,21 @@ module Grape
|
|
23
27
|
|
24
28
|
# (see ::joined_space_path)
|
25
29
|
def self.joined_space(settings)
|
26
|
-
|
30
|
+
settings&.map(&:space)
|
27
31
|
end
|
28
32
|
|
29
33
|
# Join the namespaces from a list of settings to create a path prefix.
|
30
34
|
# @param settings [Array] list of Grape::Util::InheritableSettings.
|
31
35
|
def self.joined_space_path(settings)
|
32
|
-
Grape::Router.normalize_path(joined_space(settings))
|
36
|
+
Grape::Router.normalize_path(JoinedSpaceCache[joined_space(settings)])
|
37
|
+
end
|
38
|
+
|
39
|
+
class JoinedSpaceCache < Grape::Util::Cache
|
40
|
+
def initialize
|
41
|
+
@cache = Hash.new do |h, joined_space|
|
42
|
+
h[joined_space] = -joined_space.join('/')
|
43
|
+
end
|
44
|
+
end
|
33
45
|
end
|
34
46
|
end
|
35
47
|
end
|
data/lib/grape/parser/json.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Parser
|
3
5
|
module Json
|
@@ -6,7 +8,7 @@ module Grape
|
|
6
8
|
::Grape::Json.load(object)
|
7
9
|
rescue ::Grape::Json::ParseError
|
8
10
|
# handle JSON parsing errors via the rescue handlers or provide error message
|
9
|
-
raise Grape::Exceptions::InvalidMessageBody
|
11
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/json')
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
data/lib/grape/parser/xml.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Parser
|
3
5
|
module Xml
|
@@ -6,7 +8,7 @@ module Grape
|
|
6
8
|
::Grape::Xml.parse(object)
|
7
9
|
rescue ::Grape::Xml::ParseError
|
8
10
|
# handle XML parsing errors via the rescue handlers or provide error message
|
9
|
-
raise Grape::Exceptions::InvalidMessageBody
|
11
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
data/lib/grape/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Parser
|
3
5
|
extend Util::Registrable
|
@@ -11,8 +13,8 @@ module Grape
|
|
11
13
|
}
|
12
14
|
end
|
13
15
|
|
14
|
-
def parsers(options)
|
15
|
-
builtin_parsers.merge(default_elements).merge(options[:parsers] || {})
|
16
|
+
def parsers(**options)
|
17
|
+
builtin_parsers.merge(default_elements).merge!(options[:parsers] || {})
|
16
18
|
end
|
17
19
|
|
18
20
|
def parser_for(api_format, **options)
|
data/lib/grape/path.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'grape/util/cache'
|
4
|
+
|
1
5
|
module Grape
|
2
6
|
# Represents a path to an endpoint.
|
3
7
|
class Path
|
@@ -38,11 +42,11 @@ module Grape
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def namespace?
|
41
|
-
namespace
|
45
|
+
namespace&.match?(/^\S/) && namespace != '/'
|
42
46
|
end
|
43
47
|
|
44
48
|
def path?
|
45
|
-
raw_path
|
49
|
+
raw_path&.match?(/^\S/) && raw_path != '/'
|
46
50
|
end
|
47
51
|
|
48
52
|
def suffix
|
@@ -56,7 +60,7 @@ module Grape
|
|
56
60
|
end
|
57
61
|
|
58
62
|
def path
|
59
|
-
Grape::Router.normalize_path(parts
|
63
|
+
Grape::Router.normalize_path(PartsCache[parts])
|
60
64
|
end
|
61
65
|
|
62
66
|
def path_with_suffix
|
@@ -69,6 +73,14 @@ module Grape
|
|
69
73
|
|
70
74
|
private
|
71
75
|
|
76
|
+
class PartsCache < Grape::Util::Cache
|
77
|
+
def initialize
|
78
|
+
@cache = Hash.new do |h, parts|
|
79
|
+
h[parts] = -parts.join('/')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
72
84
|
def parts
|
73
85
|
parts = [mount_path, root_prefix].compact
|
74
86
|
parts << ':version' if uses_path_versioning?
|
@@ -79,6 +91,7 @@ module Grape
|
|
79
91
|
|
80
92
|
def split_setting(key)
|
81
93
|
return if settings[key].nil?
|
94
|
+
|
82
95
|
settings[key].to_s.split('/')
|
83
96
|
end
|
84
97
|
end
|
data/lib/grape/request.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'grape/util/lazy_object'
|
4
|
+
|
1
5
|
module Grape
|
2
6
|
class Request < Rack::Request
|
3
|
-
HTTP_PREFIX = 'HTTP_'
|
7
|
+
HTTP_PREFIX = 'HTTP_'
|
4
8
|
|
5
9
|
alias rack_params params
|
6
10
|
|
7
|
-
def initialize(env, options
|
8
|
-
extend options[:build_params_with] || Grape
|
11
|
+
def initialize(env, **options)
|
12
|
+
extend options[:build_params_with] || Grape.config.param_builder
|
9
13
|
super(env)
|
10
14
|
end
|
11
15
|
|
12
16
|
def params
|
13
17
|
@params ||= build_params
|
18
|
+
rescue EOFError
|
19
|
+
raise Grape::Exceptions::EmptyMessageBody.new(content_type)
|
20
|
+
rescue Rack::Multipart::MultipartPartLimitError
|
21
|
+
raise Grape::Exceptions::TooManyMultipartFiles.new(Rack::Utils.multipart_part_limit)
|
14
22
|
end
|
15
23
|
|
16
24
|
def headers
|
@@ -28,14 +36,18 @@ module Grape
|
|
28
36
|
end
|
29
37
|
|
30
38
|
def build_headers
|
31
|
-
|
32
|
-
|
33
|
-
|
39
|
+
Grape::Util::LazyObject.new do
|
40
|
+
env.each_pair.with_object({}) do |(k, v), headers|
|
41
|
+
next unless k.to_s.start_with? HTTP_PREFIX
|
34
42
|
|
35
|
-
|
36
|
-
|
43
|
+
transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
|
44
|
+
headers[transformed_header] = v
|
45
|
+
end
|
37
46
|
end
|
38
|
-
|
47
|
+
end
|
48
|
+
|
49
|
+
def transform_header(header)
|
50
|
+
-header[5..].split('_').each(&:capitalize!).join('-')
|
39
51
|
end
|
40
52
|
end
|
41
53
|
end
|
@@ -1,30 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
class Router
|
3
5
|
# this could be an OpenStruct, but doesn't work in Ruby 2.3.0, see https://bugs.ruby-lang.org/issues/12251
|
4
6
|
class AttributeTranslator
|
5
|
-
|
7
|
+
attr_reader :attributes
|
8
|
+
|
9
|
+
ROUTE_ATTRIBUTES = %i[
|
10
|
+
prefix
|
11
|
+
version
|
12
|
+
settings
|
13
|
+
format
|
14
|
+
description
|
15
|
+
http_codes
|
16
|
+
headers
|
17
|
+
entity
|
18
|
+
details
|
19
|
+
requirements
|
20
|
+
request_method
|
21
|
+
namespace
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
ROUTER_ATTRIBUTES = %i[pattern index].freeze
|
25
|
+
|
26
|
+
def initialize(**attributes)
|
6
27
|
@attributes = attributes
|
7
28
|
end
|
8
29
|
|
30
|
+
(ROUTER_ATTRIBUTES + ROUTE_ATTRIBUTES).each do |attr|
|
31
|
+
define_method attr do
|
32
|
+
attributes[attr]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
9
36
|
def to_h
|
10
|
-
|
37
|
+
attributes
|
11
38
|
end
|
12
39
|
|
13
|
-
def method_missing(
|
14
|
-
if
|
15
|
-
|
16
|
-
|
17
|
-
|
40
|
+
def method_missing(method_name, *args)
|
41
|
+
if setter?(method_name[-1])
|
42
|
+
attributes[method_name[0..]] = *args
|
43
|
+
else
|
44
|
+
attributes[method_name]
|
18
45
|
end
|
19
46
|
end
|
20
47
|
|
21
48
|
def respond_to_missing?(method_name, _include_private = false)
|
22
|
-
if method_name[-1]
|
49
|
+
if setter?(method_name[-1])
|
23
50
|
true
|
24
51
|
else
|
25
52
|
@attributes.key?(method_name)
|
26
53
|
end
|
27
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def setter?(method_name)
|
59
|
+
method_name[-1] == '='
|
60
|
+
end
|
28
61
|
end
|
29
62
|
end
|
30
63
|
end
|
data/lib/grape/router/pattern.rb
CHANGED
@@ -1,34 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
require 'mustermann/grape'
|
5
|
+
require 'grape/util/cache'
|
3
6
|
|
4
7
|
module Grape
|
5
8
|
class Router
|
6
9
|
class Pattern
|
7
|
-
DEFAULT_PATTERN_OPTIONS = { uri_decode: true
|
10
|
+
DEFAULT_PATTERN_OPTIONS = { uri_decode: true }.freeze
|
8
11
|
DEFAULT_SUPPORTED_CAPTURE = %i[format version].freeze
|
9
12
|
|
10
|
-
attr_reader :origin, :path, :
|
13
|
+
attr_reader :origin, :path, :pattern, :to_regexp
|
11
14
|
|
12
15
|
extend Forwardable
|
13
16
|
def_delegators :pattern, :named_captures, :params
|
14
|
-
def_delegators
|
17
|
+
def_delegators :to_regexp, :===
|
15
18
|
alias match? ===
|
16
19
|
|
17
20
|
def initialize(pattern, **options)
|
18
21
|
@origin = pattern
|
19
22
|
@path = build_path(pattern, **options)
|
20
|
-
@
|
21
|
-
@
|
22
|
-
@regexp = to_regexp
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_regexp
|
26
|
-
@to_regexp ||= @pattern.to_regexp
|
23
|
+
@pattern = Mustermann::Grape.new(@path, **pattern_options(options))
|
24
|
+
@to_regexp = @pattern.to_regexp
|
27
25
|
end
|
28
26
|
|
29
27
|
private
|
30
28
|
|
31
|
-
def pattern_options
|
29
|
+
def pattern_options(options)
|
30
|
+
capture = extract_capture(**options)
|
32
31
|
options = DEFAULT_PATTERN_OPTIONS.dup
|
33
32
|
options[:capture] = capture if capture.present?
|
34
33
|
options
|
@@ -36,27 +35,32 @@ module Grape
|
|
36
35
|
|
37
36
|
def build_path(pattern, anchor: false, suffix: nil, **_options)
|
38
37
|
unless anchor || pattern.end_with?('*path')
|
38
|
+
pattern = +pattern
|
39
39
|
pattern << '/' unless pattern.end_with?('/')
|
40
40
|
pattern << '*path'
|
41
41
|
end
|
42
42
|
|
43
|
-
pattern = pattern.split('/').tap do |parts|
|
44
|
-
parts[parts.length - 1] =
|
43
|
+
pattern = -pattern.split('/').tap do |parts|
|
44
|
+
parts[parts.length - 1] = "?#{parts.last}"
|
45
45
|
end.join('/') if pattern.end_with?('*path')
|
46
46
|
|
47
|
-
pattern
|
47
|
+
PatternCache[[pattern, suffix]]
|
48
48
|
end
|
49
49
|
|
50
50
|
def extract_capture(requirements: {}, **options)
|
51
51
|
requirements = {}.merge(requirements)
|
52
|
-
|
52
|
+
DEFAULT_SUPPORTED_CAPTURE.each_with_object(requirements) do |field, capture|
|
53
53
|
option = Array(options[field])
|
54
54
|
capture[field] = option.map(&:to_s) if option.present?
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
58
|
+
class PatternCache < Grape::Util::Cache
|
59
|
+
def initialize
|
60
|
+
@cache = Hash.new do |h, (pattern, suffix)|
|
61
|
+
h[[pattern, suffix]] = -"#{pattern}#{suffix}"
|
62
|
+
end
|
63
|
+
end
|
60
64
|
end
|
61
65
|
end
|
62
66
|
end
|