grape 1.8.0 → 4.0.1
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 +517 -1
- data/CONTRIBUTING.md +19 -11
- data/README.md +841 -545
- data/UPGRADING.md +1553 -7
- data/grape.gemspec +11 -14
- data/lib/grape/api/instance.rb +77 -160
- data/lib/grape/api.rb +75 -107
- data/lib/grape/content_types.rb +67 -9
- 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 +8 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +25 -63
- data/lib/grape/dsl/entity.rb +106 -0
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +83 -64
- data/lib/grape/dsl/inside_route.rb +69 -296
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +88 -94
- data/lib/grape/dsl/request_response.rb +143 -147
- data/lib/grape/dsl/rescue_options.rb +25 -0
- data/lib/grape/dsl/routing.rb +333 -191
- 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 +25 -0
- data/lib/grape/endpoint.rb +319 -269
- data/lib/grape/{util/env.rb → env.rb} +8 -8
- data/lib/grape/error_formatter/base.rb +57 -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 +9 -25
- data/lib/grape/exceptions/base.rb +22 -58
- data/lib/grape/exceptions/error_response.rb +48 -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_error_formatter.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 +28 -11
- 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 +5 -6
- data/lib/grape/formatter/serializable_hash.rb +7 -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 +44 -42
- data/lib/grape/middleware/auth/base.rb +11 -33
- data/lib/grape/middleware/auth/dsl.rb +12 -34
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +56 -33
- data/lib/grape/middleware/error.rb +290 -91
- data/lib/grape/middleware/formatter.rb +164 -106
- data/lib/grape/middleware/precomputed_content_types.rb +51 -0
- data/lib/grape/middleware/stack.rb +38 -40
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +66 -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 +48 -25
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +21 -14
- 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 +8 -94
- data/lib/grape/precompiled_json.rb +50 -0
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +200 -26
- data/lib/grape/router/base_route.rb +85 -0
- data/lib/grape/router/greedy_route.rb +30 -0
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern/path.rb +78 -0
- data/lib/grape/router/pattern.rb +77 -35
- data/lib/grape/router/route.rb +79 -60
- data/lib/grape/router.rb +128 -104
- data/lib/grape/serve_stream/file_body.rb +7 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +7 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +67 -0
- data/lib/grape/util/cache.rb +22 -5
- data/lib/grape/util/deep_freeze.rb +34 -0
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/freeze_on_new.rb +20 -0
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +777 -39
- 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 +74 -0
- data/lib/grape/util/path_normalizer.rb +37 -0
- data/lib/grape/util/registry.rb +37 -0
- data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
- data/lib/grape/util/stackable_values.rb +40 -16
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +61 -28
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +29 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +35 -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 +223 -306
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +6 -4
- data/lib/grape/validations/types/array_coercer.rb +7 -12
- data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +17 -28
- data/lib/grape/validations/types/json.rb +1 -5
- data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
- data/lib/grape/validations/types/primitive_coercer.rb +14 -35
- data/lib/grape/validations/types/set_coercer.rb +1 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
- 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 +118 -37
- data/lib/grape/validations/validators/coerce_validator.rb +26 -38
- data/lib/grape/validations/validators/contract_scope_validator.rb +46 -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 +50 -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 +51 -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 +7 -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 +96 -288
- metadata +83 -294
- 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/base_inheritable.rb +0 -43
- data/lib/grape/util/inheritable_values.rb +0 -31
- 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/reverse_stackable_values.rb +0 -20
- 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
data/lib/grape/endpoint.rb
CHANGED
|
@@ -6,136 +6,94 @@ module Grape
|
|
|
6
6
|
# on the instance level of this class may be called
|
|
7
7
|
# from inside a `get`, `post`, etc.
|
|
8
8
|
class Endpoint
|
|
9
|
+
extend Forwardable
|
|
9
10
|
include Grape::DSL::Settings
|
|
11
|
+
include Grape::DSL::Headers
|
|
10
12
|
include Grape::DSL::InsideRoute
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
attr_reader :env, :request, :source, :options, :endpoints
|
|
15
|
+
attr_accessor :options_route_enabled
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
self == Endpoint ? Class.new(Endpoint).new(*args, &block) : super
|
|
18
|
-
end
|
|
17
|
+
def_delegators :request, :params, :headers, :cookies
|
|
18
|
+
def_delegator :cookies, :response_cookies
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if new_setup == false
|
|
23
|
-
return @before_each unless block
|
|
20
|
+
# The API (a +Grape::API+ instance) this endpoint belongs to.
|
|
21
|
+
def_delegator :@config, :api
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
23
|
+
# The logger configured on the API this endpoint belongs to. Available
|
|
24
|
+
# inside route handlers, +before+/+after+/+after_validation+/+finally+
|
|
25
|
+
# filters, and +rescue_from+ blocks.
|
|
26
|
+
def_delegator :api, :logger
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
# The Rack app or Grape API mounted at this endpoint, or +nil+ for a plain
|
|
29
|
+
# block endpoint. Prefer this over +options[:app]+, which is retained only
|
|
30
|
+
# for backwards compatibility.
|
|
31
|
+
def mounted_app
|
|
32
|
+
config.app
|
|
33
|
+
end
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# instance of +Endpoint+ to bind to the method during the call.
|
|
45
|
-
#
|
|
46
|
-
# @param [String, Symbol] method_name
|
|
47
|
-
# @return [Proc]
|
|
48
|
-
# @raise [NameError] an instance method with the same name already exists
|
|
49
|
-
def generate_api_method(method_name, &block)
|
|
50
|
-
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
|
|
51
|
-
|
|
52
|
-
define_method(method_name, &block)
|
|
53
|
-
method = instance_method(method_name)
|
|
54
|
-
remove_method(method_name)
|
|
55
|
-
|
|
56
|
-
proc do |endpoint_instance|
|
|
57
|
-
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
|
|
58
|
-
method.bind(endpoint_instance).call
|
|
59
|
-
end
|
|
60
|
-
end
|
|
35
|
+
class << self
|
|
36
|
+
def block_to_unbound_method(block)
|
|
37
|
+
return unless block
|
|
38
|
+
|
|
39
|
+
define_method :temp_unbound_method, block
|
|
40
|
+
method = instance_method(:temp_unbound_method)
|
|
41
|
+
remove_method :temp_unbound_method
|
|
42
|
+
method
|
|
61
43
|
end
|
|
62
44
|
end
|
|
63
45
|
|
|
64
46
|
# Create a new endpoint.
|
|
65
47
|
# @param new_settings [InheritableSetting] settings to determine the params,
|
|
66
48
|
# validations, and other properties from.
|
|
67
|
-
# @param
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
49
|
+
# @param http_methods [String or Array] which HTTP method(s) can be used to
|
|
50
|
+
# reach this endpoint.
|
|
51
|
+
# @param path [String or Array] the path to this endpoint, within the
|
|
52
|
+
# current scope.
|
|
53
|
+
# @param api [Grape::API] the API this endpoint belongs to. Exposed as
|
|
54
|
+
# {#api}.
|
|
55
|
+
# @param app [#call, nil] the Rack app or Grape API mounted at this
|
|
56
|
+
# endpoint; +nil+ for a plain block endpoint. Exposed as {#mounted_app}.
|
|
57
|
+
# @param params [Hash] the declared params for this endpoint, keyed by name.
|
|
58
|
+
# Kept out of +route_options+ and read via +config.params+.
|
|
59
|
+
# @param requirements [Hash, nil] regular-expression constraints for named
|
|
60
|
+
# path params. Read via +config.requirements+.
|
|
61
|
+
# @param anchor [Boolean] whether the route anchors to the whole path
|
|
62
|
+
# (default +true+). Read via +config.anchor+.
|
|
63
|
+
# @param options [Hash] attributes of this endpoint, normalized into a
|
|
64
|
+
# +Grape::Endpoint::Options+ value object.
|
|
72
65
|
# @option options route_options [Hash]
|
|
73
66
|
# @note This happens at the time of API definition, so in this context the
|
|
74
67
|
# endpoint does not know if it will be mounted under a different endpoint.
|
|
75
68
|
# @yield a block defining what your API should do when this endpoint is hit
|
|
76
|
-
def initialize(new_settings,
|
|
77
|
-
|
|
78
|
-
require_option(options, :method)
|
|
79
|
-
|
|
80
|
-
self.inheritable_setting = new_settings.point_in_time_copy
|
|
81
|
-
|
|
82
|
-
# now +namespace_stackable(:declared_params)+ contains all params defined for
|
|
83
|
-
# this endpoint and its parents, but later it will be cleaned up,
|
|
84
|
-
# see +reset_validations!+ in lib/grape/dsl/validations.rb
|
|
85
|
-
route_setting(:declared_params, namespace_stackable(:declared_params).flatten)
|
|
86
|
-
route_setting(:saved_validations, namespace_stackable(:validations))
|
|
87
|
-
|
|
88
|
-
namespace_stackable(:representations, []) unless namespace_stackable(:representations)
|
|
89
|
-
namespace_inheritable(:default_error_status, 500) unless namespace_inheritable(:default_error_status)
|
|
69
|
+
def initialize(new_settings, http_methods:, path:, api:, app: nil, params: {}, requirements: nil, anchor: true, **options, &block)
|
|
70
|
+
self.inheritable_setting = new_settings.point_in_time_copy_for_endpoint
|
|
90
71
|
|
|
91
72
|
@options = options
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@options[:method] = Array(options[:method])
|
|
97
|
-
@options[:route_options] ||= {}
|
|
98
|
-
|
|
99
|
-
@lazy_initialize_lock = Mutex.new
|
|
100
|
-
@lazy_initialized = nil
|
|
101
|
-
@block = nil
|
|
73
|
+
@config = Options.new(http_methods:, path:, api:, app:, params:, requirements:, anchor:, **options)
|
|
74
|
+
# +:app+ is still surfaced on the public options Hash for backwards
|
|
75
|
+
# compatibility (e.g. grape-swagger); prefer the +mounted_app+ reader.
|
|
76
|
+
@options[:app] = app if app
|
|
102
77
|
|
|
103
78
|
@status = nil
|
|
104
79
|
@stream = nil
|
|
105
80
|
@body = nil
|
|
106
|
-
@
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@source = block
|
|
111
|
-
@block = self.class.generate_api_method(method_name, &block)
|
|
81
|
+
@source = self.class.block_to_unbound_method(block)
|
|
82
|
+
@before_filter_passed = false
|
|
83
|
+
@options_route_enabled = false
|
|
84
|
+
@endpoints = @config.app.endpoints if @config.app.respond_to?(:endpoints)
|
|
112
85
|
end
|
|
113
86
|
|
|
114
|
-
# Update our settings from a given
|
|
87
|
+
# Update our settings from a given parent settings instance. Used when
|
|
115
88
|
# the endpoint's API is mounted under another one.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
|
121
|
-
|
|
122
|
-
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def require_option(options, key)
|
|
126
|
-
raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def method_name
|
|
130
|
-
[options[:method],
|
|
131
|
-
Namespace.joined_space(namespace_stackable(:namespace)),
|
|
132
|
-
(namespace_stackable(:mount_path) || []).join('/'),
|
|
133
|
-
options[:path].join('/')]
|
|
134
|
-
.join(' ')
|
|
89
|
+
# @param settings [Grape::Util::InheritableSetting]
|
|
90
|
+
def inherit_settings(settings)
|
|
91
|
+
inheritable_setting.inherit_route_params(settings)
|
|
92
|
+
endpoints&.each { |e| e.inherit_settings(settings) }
|
|
135
93
|
end
|
|
136
94
|
|
|
137
95
|
def routes
|
|
138
|
-
@routes ||= endpoints
|
|
96
|
+
@routes ||= endpoints&.flat_map(&:routes) || to_routes
|
|
139
97
|
end
|
|
140
98
|
|
|
141
99
|
def reset_routes!
|
|
@@ -146,76 +104,26 @@ module Grape
|
|
|
146
104
|
|
|
147
105
|
def mount_in(router)
|
|
148
106
|
if endpoints
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
reset_routes!
|
|
152
|
-
routes.each do |route|
|
|
153
|
-
methods = [route.request_method]
|
|
154
|
-
methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
|
155
|
-
methods.each do |method|
|
|
156
|
-
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
|
|
157
|
-
router.append(route.apply(self))
|
|
158
|
-
end
|
|
159
|
-
end
|
|
107
|
+
compile!
|
|
108
|
+
return endpoints.each { |e| e.mount_in(router) }
|
|
160
109
|
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def to_routes
|
|
164
|
-
route_options = prepare_default_route_attributes
|
|
165
|
-
map_routes do |method, path|
|
|
166
|
-
path = prepare_path(path)
|
|
167
|
-
params = merge_route_options(**route_options.merge(suffix: path.suffix))
|
|
168
|
-
route = Router::Route.new(method, path.path, **params)
|
|
169
|
-
route.apply(self)
|
|
170
|
-
end.flatten
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def prepare_routes_requirements
|
|
174
|
-
{}.merge!(*namespace_stackable(:namespace).map(&:requirements)).tap do |requirements|
|
|
175
|
-
endpoint_requirements = options.dig(:route_options, :requirements)
|
|
176
|
-
requirements.merge!(endpoint_requirements) if endpoint_requirements
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def prepare_default_route_attributes
|
|
181
|
-
{
|
|
182
|
-
namespace: namespace,
|
|
183
|
-
version: prepare_version,
|
|
184
|
-
requirements: prepare_routes_requirements,
|
|
185
|
-
prefix: namespace_inheritable(:root_prefix),
|
|
186
|
-
anchor: options[:route_options].fetch(:anchor, true),
|
|
187
|
-
settings: inheritable_setting.route.except(:declared_params, :saved_validations),
|
|
188
|
-
forward_match: options[:forward_match]
|
|
189
|
-
}
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def prepare_version
|
|
193
|
-
version = namespace_inheritable(:version) || []
|
|
194
|
-
return if version.empty?
|
|
195
110
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
end
|
|
111
|
+
reset_routes!
|
|
112
|
+
compile!
|
|
113
|
+
routes.each do |route|
|
|
114
|
+
router.append(route.apply(self))
|
|
115
|
+
next if inheritable_setting.do_not_route_head? || route.request_method != Rack::GET
|
|
202
116
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def prepare_path(path)
|
|
208
|
-
path_settings = inheritable_setting.to_hash[:namespace_stackable].merge(inheritable_setting.to_hash[:namespace_inheritable])
|
|
209
|
-
Path.prepare(path, namespace, path_settings)
|
|
117
|
+
router.append(route.to_head.apply(self))
|
|
118
|
+
end
|
|
210
119
|
end
|
|
211
120
|
|
|
212
121
|
def namespace
|
|
213
|
-
@namespace ||=
|
|
122
|
+
@namespace ||= inheritable_setting.namespace_path
|
|
214
123
|
end
|
|
215
124
|
|
|
216
125
|
def call(env)
|
|
217
|
-
|
|
218
|
-
dup.call!(env)
|
|
126
|
+
@prototype.dup.call!(env)
|
|
219
127
|
end
|
|
220
128
|
|
|
221
129
|
def call!(env)
|
|
@@ -224,44 +132,54 @@ module Grape
|
|
|
224
132
|
@app.call(env)
|
|
225
133
|
end
|
|
226
134
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
135
|
+
def ==(other)
|
|
136
|
+
other.is_a?(self.class) &&
|
|
137
|
+
config == other.config &&
|
|
138
|
+
inheritable_setting == other.inheritable_setting
|
|
231
139
|
end
|
|
140
|
+
alias eql? ==
|
|
232
141
|
|
|
233
|
-
|
|
234
|
-
|
|
142
|
+
# Mirrors #==. The class stays out: #== admits a subclass instance
|
|
143
|
+
# through is_a?, and such a pair must hash alike. The block (#source) is
|
|
144
|
+
# not part of either, matching the long-standing duplicate-route check in
|
|
145
|
+
# DSL::Routing#route.
|
|
146
|
+
def hash
|
|
147
|
+
[config, inheritable_setting].hash
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# The purpose of this override is solely for stripping internals when an error occurs while calling
|
|
151
|
+
# an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
|
|
152
|
+
# Otherwise, it calls super.
|
|
153
|
+
def inspect
|
|
154
|
+
return super unless env
|
|
155
|
+
|
|
156
|
+
"#{self.class} in '#{route.origin}' endpoint"
|
|
235
157
|
end
|
|
236
158
|
|
|
237
159
|
protected
|
|
238
160
|
|
|
239
161
|
def run
|
|
240
|
-
|
|
241
|
-
@
|
|
242
|
-
@request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
|
|
243
|
-
@params = @request.params
|
|
244
|
-
@headers = @request.headers
|
|
162
|
+
instrument_run do
|
|
163
|
+
@request = Grape::Request.new(env, build_params_with: @build_params_with)
|
|
245
164
|
begin
|
|
246
|
-
cookies.read(@request)
|
|
247
|
-
self.class.run_before_each(self)
|
|
248
165
|
run_filters befores, :before
|
|
166
|
+
@before_filter_passed = true
|
|
249
167
|
|
|
250
|
-
if (
|
|
251
|
-
|
|
168
|
+
if env.key?(Grape::Env::GRAPE_ALLOWED_METHODS)
|
|
169
|
+
header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
|
|
170
|
+
raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?
|
|
252
171
|
|
|
253
|
-
header 'Allow', allowed_methods
|
|
254
172
|
response_object = ''
|
|
255
173
|
status 204
|
|
256
174
|
else
|
|
257
175
|
run_filters before_validations, :before_validation
|
|
258
|
-
run_validators
|
|
176
|
+
run_validators(request:)
|
|
259
177
|
run_filters after_validations, :after_validation
|
|
260
178
|
response_object = execute
|
|
261
179
|
end
|
|
262
180
|
|
|
263
181
|
run_filters afters, :after
|
|
264
|
-
|
|
182
|
+
build_response_cookies
|
|
265
183
|
|
|
266
184
|
# status verifies body presence when DELETE
|
|
267
185
|
@body ||= response_object
|
|
@@ -276,132 +194,264 @@ module Grape
|
|
|
276
194
|
end
|
|
277
195
|
end
|
|
278
196
|
|
|
279
|
-
def
|
|
280
|
-
|
|
197
|
+
def execute
|
|
198
|
+
return unless source
|
|
281
199
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
helpers: helpers,
|
|
285
|
-
format: namespace_inheritable(:format),
|
|
286
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
|
287
|
-
default_status: namespace_inheritable(:default_error_status),
|
|
288
|
-
rescue_all: namespace_inheritable(:rescue_all),
|
|
289
|
-
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
|
290
|
-
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
|
291
|
-
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
|
292
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
|
|
293
|
-
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
|
|
294
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
|
|
295
|
-
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
|
296
|
-
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
|
297
|
-
|
|
298
|
-
stack.concat namespace_stackable(:middleware)
|
|
299
|
-
|
|
300
|
-
if namespace_inheritable(:version)
|
|
301
|
-
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
|
302
|
-
versions: namespace_inheritable(:version)&.flatten,
|
|
303
|
-
version_options: namespace_inheritable(:version_options),
|
|
304
|
-
prefix: namespace_inheritable(:root_prefix),
|
|
305
|
-
mount_path: namespace_stackable(:mount_path).first
|
|
200
|
+
instrument_render do
|
|
201
|
+
source.bind_call(self)
|
|
306
202
|
end
|
|
203
|
+
end
|
|
307
204
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
|
312
|
-
formatters: namespace_stackable_with_hash(:formatters),
|
|
313
|
-
parsers: namespace_stackable_with_hash(:parsers)
|
|
205
|
+
def run_validators(request:)
|
|
206
|
+
validators = inheritable_setting.route_validations
|
|
207
|
+
return if validators.blank?
|
|
314
208
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
209
|
+
validation_exceptions = nil
|
|
210
|
+
|
|
211
|
+
Grape::Validations::ParamScopeTracker.track do
|
|
212
|
+
instrument_run_validators(validators, request) do
|
|
213
|
+
validators.each do |validator|
|
|
214
|
+
validator.validate(request)
|
|
215
|
+
rescue Grape::Exceptions::Validation, Grape::Exceptions::ValidationArrayErrors => e
|
|
216
|
+
(validation_exceptions ||= []) << e
|
|
217
|
+
break if validator.fail_fast?
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
raise Grape::Exceptions::ValidationErrors.new(exceptions: validation_exceptions, headers: header) if validation_exceptions
|
|
318
223
|
end
|
|
319
224
|
|
|
320
|
-
def
|
|
321
|
-
|
|
322
|
-
|
|
225
|
+
def run_filters(filters, type = :other)
|
|
226
|
+
return if filters.blank?
|
|
227
|
+
|
|
228
|
+
instrument_run_filters(filters, type) do
|
|
229
|
+
filters.each { |filter| instance_eval(&filter) }
|
|
230
|
+
end
|
|
323
231
|
end
|
|
324
232
|
|
|
325
|
-
|
|
233
|
+
attr_reader :befores, :before_validations, :after_validations, :afters, :finallies, :config
|
|
326
234
|
|
|
327
|
-
def
|
|
328
|
-
|
|
235
|
+
def options?
|
|
236
|
+
options_route_enabled && env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
attr_reader :before_filter_passed
|
|
242
|
+
|
|
243
|
+
# Instrument helpers. Each guards on +listening?+ so that with no subscriber
|
|
244
|
+
# the payload Hash and notification machinery are skipped and the block runs
|
|
245
|
+
# directly (no added allocations); the block is forwarded anonymously so
|
|
246
|
+
# nothing is allocated unless a subscriber is present.
|
|
247
|
+
def instrument_run(&)
|
|
248
|
+
return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_run.grape')
|
|
249
|
+
|
|
250
|
+
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env:, &)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def instrument_render(&)
|
|
254
|
+
return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_render.grape')
|
|
255
|
+
|
|
256
|
+
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: self, &)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def instrument_run_validators(validators, request, &)
|
|
260
|
+
return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_run_validators.grape')
|
|
261
|
+
|
|
262
|
+
ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators:, request:, &)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def instrument_run_filters(filters, type, &)
|
|
266
|
+
return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_run_filters.grape')
|
|
267
|
+
|
|
268
|
+
ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters:, type:, &)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def compile!
|
|
272
|
+
@app = config.app || build_stack
|
|
273
|
+
warn_unauthenticated_mounted_app
|
|
274
|
+
@helpers = build_helpers
|
|
275
|
+
callbacks = inheritable_setting.callbacks
|
|
276
|
+
@befores = callbacks.fetch(:before)
|
|
277
|
+
@before_validations = callbacks.fetch(:before_validation)
|
|
278
|
+
@after_validations = callbacks.fetch(:after_validation)
|
|
279
|
+
@afters = callbacks.fetch(:after)
|
|
280
|
+
@finallies = callbacks.fetch(:finally)
|
|
281
|
+
@build_params_with = inheritable_setting.build_params_with
|
|
282
|
+
@prototype = build_prototype
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# The object each request is copied from.
|
|
286
|
+
#
|
|
287
|
+
# Helpers reach an endpoint by module inclusion, and +Object#dup+ does not
|
|
288
|
+
# carry a singleton class over — so including them on the per-request copy
|
|
289
|
+
# meant giving every request a brand-new singleton class, whose method
|
|
290
|
+
# cache then started cold for the helpers, the route block and every DSL
|
|
291
|
+
# method the endpoint answers. Include them once into a subclass owned by
|
|
292
|
+
# this endpoint instead, and copy this endpoint's state onto an instance of
|
|
293
|
+
# it: the per-request copy already answers to the helpers, against a class
|
|
294
|
+
# that has been warm since boot. It also keeps request handling free of
|
|
295
|
+
# class mutation.
|
|
296
|
+
#
|
|
297
|
+
# Endpoints with no helpers are their own prototype, exactly as before.
|
|
298
|
+
#
|
|
299
|
+
# +allocate+ rather than +new+ because the constructor rebuilds state from
|
|
300
|
+
# the DSL's arguments — a fresh settings copy, a fresh +@config+, another
|
|
301
|
+
# +block_to_unbound_method+ — and would still not hold what +compile!+ has
|
|
302
|
+
# just computed. What is wanted is what +dup+ itself does (allocate, then
|
|
303
|
+
# copy the ivars over) across a class boundary, which neither +dup+ nor
|
|
304
|
+
# +initialize_copy+ will do: both insist on the receiver's own class. So
|
|
305
|
+
# the copy is written out here. Taking all of them is safe because
|
|
306
|
+
# everything set past this point (+routes+, +namespace+) is route-building
|
|
307
|
+
# memoization the request path never reads.
|
|
308
|
+
def build_prototype
|
|
309
|
+
return self unless @helpers
|
|
310
|
+
|
|
311
|
+
klass = Class.new(self.class)
|
|
312
|
+
# Anonymous classes answer nil to +name+ and render as a bare address,
|
|
313
|
+
# which would reach anything that reports on the endpoint — +inspect+
|
|
314
|
+
# here, a logger or an error tracker elsewhere. The annotation is what
|
|
315
|
+
# +set_temporary_name+ is for; a plain constant path is rejected, which
|
|
316
|
+
# is right, since this class is not reachable under that name.
|
|
317
|
+
klass.set_temporary_name("#{self.class}(helpers)")
|
|
318
|
+
klass.include(@helpers)
|
|
319
|
+
prototype = klass.allocate
|
|
320
|
+
# Everything but the prototype slot itself, so that recompiling (a
|
|
321
|
+
# remount) does not leave the new prototype holding the one it replaces.
|
|
322
|
+
(instance_variables - [:@prototype]).each { |name| prototype.instance_variable_set(name, instance_variable_get(name)) }
|
|
323
|
+
prototype
|
|
329
324
|
end
|
|
330
325
|
|
|
331
|
-
def
|
|
332
|
-
|
|
326
|
+
def to_routes
|
|
327
|
+
route_options = config.route_options
|
|
328
|
+
params = config.params
|
|
329
|
+
path_settings = inheritable_setting.path_settings
|
|
330
|
+
forward_match = bare_rack_app?
|
|
331
|
+
version = prepare_version(inheritable_setting.version)
|
|
332
|
+
prefix = inheritable_setting.root_prefix
|
|
333
|
+
requirements = prepare_routes_requirements(config.requirements)
|
|
334
|
+
anchor = config.anchor
|
|
335
|
+
settings = inheritable_setting.route_settings
|
|
336
|
+
|
|
337
|
+
config.http_methods.flat_map do |method|
|
|
338
|
+
config.path.map do |path|
|
|
339
|
+
pattern = Grape::Router::Pattern.build(
|
|
340
|
+
path:,
|
|
341
|
+
namespace:,
|
|
342
|
+
settings: path_settings,
|
|
343
|
+
anchor:,
|
|
344
|
+
params:,
|
|
345
|
+
version:,
|
|
346
|
+
requirements:
|
|
347
|
+
)
|
|
348
|
+
Grape::Router::Route.new(self, method, pattern, route_options, forward_match:, params:, namespace:, prefix:, settings:)
|
|
349
|
+
end
|
|
350
|
+
end
|
|
333
351
|
end
|
|
334
352
|
|
|
335
|
-
|
|
336
|
-
|
|
353
|
+
# True when a bare Rack app (anything that isn't a Grape app) is mounted at
|
|
354
|
+
# this endpoint. Such an app is called directly and matched by path prefix
|
|
355
|
+
# rather than an anchored route.
|
|
356
|
+
def bare_rack_app?
|
|
357
|
+
config.app && !config.app.is_a?(Grape::Mountable)
|
|
358
|
+
end
|
|
337
359
|
|
|
338
|
-
|
|
339
|
-
|
|
360
|
+
def prepare_routes_requirements(route_options_requirements)
|
|
361
|
+
namespace_requirements = inheritable_setting.namespace_requirements
|
|
362
|
+
namespace_requirements << route_options_requirements if route_options_requirements.present?
|
|
363
|
+
namespace_requirements.reduce({}, :merge)
|
|
364
|
+
end
|
|
340
365
|
|
|
341
|
-
|
|
342
|
-
|
|
366
|
+
def prepare_version(namespace_inheritable_version)
|
|
367
|
+
return if namespace_inheritable_version.blank?
|
|
343
368
|
|
|
344
|
-
|
|
345
|
-
end
|
|
369
|
+
namespace_inheritable_version.length == 1 ? namespace_inheritable_version.first : namespace_inheritable_version
|
|
346
370
|
end
|
|
347
371
|
|
|
348
|
-
def
|
|
349
|
-
|
|
372
|
+
def build_stack
|
|
373
|
+
stack = Grape::Middleware::Stack.new
|
|
350
374
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
375
|
+
content_types = inheritable_setting.content_types
|
|
376
|
+
format = inheritable_setting.format
|
|
377
|
+
|
|
378
|
+
stack.use Rack::Head
|
|
379
|
+
stack.use Rack::Lint if lint?
|
|
380
|
+
stack.use Grape::Middleware::Error, **error_middleware_options(format, content_types)
|
|
381
|
+
|
|
382
|
+
stack.concat inheritable_setting.middleware
|
|
383
|
+
|
|
384
|
+
if inheritable_setting.version.present?
|
|
385
|
+
version_options = inheritable_setting.version_options
|
|
386
|
+
stack.use Grape::Middleware::Versioner.using(version_options.using),
|
|
387
|
+
versions: inheritable_setting.version.flatten,
|
|
388
|
+
version_options:,
|
|
389
|
+
prefix: inheritable_setting.root_prefix,
|
|
390
|
+
mount_path: inheritable_setting.mount_path
|
|
361
391
|
end
|
|
362
392
|
|
|
363
|
-
|
|
364
|
-
|
|
393
|
+
stack.use Grape::Middleware::Formatter,
|
|
394
|
+
format:,
|
|
395
|
+
default_format: inheritable_setting.default_format || :txt,
|
|
396
|
+
content_types:,
|
|
397
|
+
formatters: inheritable_setting.formatters,
|
|
398
|
+
parsers: inheritable_setting.parsers
|
|
365
399
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
end
|
|
370
|
-
post_extension = DSL::InsideRoute.post_filter_methods(type)
|
|
371
|
-
extend post_extension if post_extension
|
|
400
|
+
builder = stack.build
|
|
401
|
+
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
|
402
|
+
builder.to_app
|
|
372
403
|
end
|
|
373
404
|
|
|
374
|
-
def
|
|
375
|
-
|
|
405
|
+
def error_middleware_options(format, content_types)
|
|
406
|
+
setting = inheritable_setting
|
|
407
|
+
{
|
|
408
|
+
format:,
|
|
409
|
+
content_types:,
|
|
410
|
+
default_status: setting.default_error_status,
|
|
411
|
+
rescue_all: setting.rescue_all?,
|
|
412
|
+
rescue_grape_exceptions: setting.rescue_grape_exceptions?,
|
|
413
|
+
default_error_formatter: setting.default_error_formatter,
|
|
414
|
+
error_formatters: setting.error_formatters,
|
|
415
|
+
rescue_options: setting.rescue_options,
|
|
416
|
+
rescue_handlers: setting.rescue_handlers,
|
|
417
|
+
base_only_rescue_handlers: setting.base_only_rescue_handlers,
|
|
418
|
+
all_rescue_handler: setting.all_rescue_handler,
|
|
419
|
+
grape_exceptions_rescue_handler: setting.grape_exceptions_rescue_handler,
|
|
420
|
+
internal_grape_exceptions_rescue_handler: setting.internal_grape_exceptions_rescue_handler
|
|
421
|
+
}
|
|
376
422
|
end
|
|
377
423
|
|
|
378
|
-
def
|
|
379
|
-
|
|
380
|
-
|
|
424
|
+
def build_helpers
|
|
425
|
+
helpers = inheritable_setting.helpers
|
|
426
|
+
return if helpers.empty?
|
|
381
427
|
|
|
382
|
-
|
|
383
|
-
namespace_stackable(:after_validations)
|
|
428
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
|
384
429
|
end
|
|
385
430
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
431
|
+
# A bare Rack app mounted with +mount+ is called directly (see +compile!+):
|
|
432
|
+
# it does not go through +build_stack+, so the API's authentication
|
|
433
|
+
# middleware never runs and the mount is reachable unauthenticated. Mounted
|
|
434
|
+
# Grape APIs are unaffected because they rebuild their own stack from the
|
|
435
|
+
# inherited settings. Warn so this bypass isn't silent.
|
|
436
|
+
def warn_unauthenticated_mounted_app
|
|
437
|
+
return unless bare_rack_app?
|
|
438
|
+
return unless inheritable_setting.auth
|
|
389
439
|
|
|
390
|
-
|
|
391
|
-
|
|
440
|
+
warn "Grape: #{config.app} is mounted under an API that declares authentication, but authentication " \
|
|
441
|
+
'middleware does not wrap mounted Rack applications. Requests to this mount are not authenticated by Grape.'
|
|
392
442
|
end
|
|
393
443
|
|
|
394
|
-
def
|
|
395
|
-
return
|
|
444
|
+
def build_response_cookies
|
|
445
|
+
return unless request.cookies?
|
|
396
446
|
|
|
397
|
-
|
|
398
|
-
|
|
447
|
+
response_cookies do |name, value|
|
|
448
|
+
cookie_value = value.is_a?(Hash) ? value : { value: }
|
|
449
|
+
Rack::Utils.set_cookie_header! header, name, cookie_value
|
|
399
450
|
end
|
|
400
451
|
end
|
|
401
452
|
|
|
402
|
-
def
|
|
403
|
-
|
|
404
|
-
env[Grape::Http::Headers::REQUEST_METHOD] == Grape::Http::Headers::OPTIONS
|
|
453
|
+
def lint?
|
|
454
|
+
inheritable_setting.lint? || Grape.config.lint
|
|
405
455
|
end
|
|
406
456
|
end
|
|
407
457
|
end
|