grape 1.1.0 → 1.5.3
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 +278 -44
- data/LICENSE +1 -1
- data/README.md +514 -69
- data/UPGRADING.md +424 -17
- data/grape.gemspec +13 -2
- data/lib/grape.rb +104 -71
- data/lib/grape/api.rb +138 -175
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +283 -0
- data/lib/grape/config.rb +34 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/api.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +22 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +41 -7
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +5 -2
- data/lib/grape/dsl/inside_route.rb +92 -49
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +9 -0
- data/lib/grape/dsl/parameters.rb +25 -14
- data/lib/grape/dsl/request_response.rb +4 -2
- data/lib/grape/dsl/routing.rb +17 -10
- data/lib/grape/dsl/settings.rb +7 -1
- data/lib/grape/dsl/validations.rb +24 -4
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +59 -35
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +2 -0
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +2 -0
- data/lib/grape/exceptions/base.rb +20 -14
- 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 +2 -0
- 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/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 +2 -0
- data/lib/grape/exceptions/validation.rb +4 -2
- 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.rb +5 -3
- data/lib/grape/formatter/json.rb +2 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -0
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +2 -0
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/locale/en.yml +3 -1
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +2 -0
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +10 -7
- data/lib/grape/middleware/error.rb +21 -16
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +8 -6
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +13 -3
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
- data/lib/grape/middleware/versioner/header.rb +10 -8
- data/lib/grape/middleware/versioner/param.rb +3 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
- data/lib/grape/middleware/versioner/path.rb +3 -1
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/parser/json.rb +3 -1
- data/lib/grape/parser/xml.rb +3 -1
- data/lib/grape/path.rb +15 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +19 -10
- data/lib/grape/router.rb +30 -29
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +14 -28
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +2 -0
- data/lib/grape/util/inheritable_values.rb +7 -25
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_block.rb +27 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +98 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +10 -35
- data/lib/grape/util/stackable_values.rb +21 -34
- data/lib/grape/util/strict_hash_configuration.rb +2 -0
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations.rb +2 -0
- data/lib/grape/validations/attributes_iterator.rb +16 -6
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +51 -30
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types.rb +13 -38
- data/lib/grape/validations/types/array_coercer.rb +65 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +46 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +67 -0
- data/lib/grape/validations/types/set_coercer.rb +40 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none.rb +8 -13
- data/lib/grape/validations/validators/allow_blank.rb +3 -1
- data/lib/grape/validations/validators/as.rb +5 -4
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
- data/lib/grape/validations/validators/base.rb +20 -16
- data/lib/grape/validations/validators/coerce.rb +46 -29
- data/lib/grape/validations/validators/default.rb +6 -6
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
- data/lib/grape/validations/validators/except_values.rb +4 -2
- data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
- data/lib/grape/validations/validators/presence.rb +3 -1
- data/lib/grape/validations/validators/regexp.rb +4 -2
- data/lib/grape/validations/validators/same_as.rb +26 -0
- data/lib/grape/validations/validators/values.rb +18 -6
- data/lib/grape/version.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +5 -3
- data/spec/grape/api/deeply_included_options_spec.rb +2 -0
- data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
- data/spec/grape/api/inherited_helpers_spec.rb +2 -0
- data/spec/grape/api/instance_spec.rb +104 -0
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/nested_helpers_spec.rb +2 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/parameters_modification_spec.rb +3 -1
- data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +2 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_spec.rb +2 -0
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +565 -12
- data/spec/grape/config_spec.rb +19 -0
- data/spec/grape/dsl/callbacks_spec.rb +2 -0
- data/spec/grape/dsl/configuration_spec.rb +2 -0
- data/spec/grape/dsl/desc_spec.rb +42 -16
- data/spec/grape/dsl/headers_spec.rb +2 -0
- data/spec/grape/dsl/helpers_spec.rb +4 -2
- data/spec/grape/dsl/inside_route_spec.rb +184 -33
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +10 -0
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +2 -0
- data/spec/grape/dsl/routing_spec.rb +12 -0
- data/spec/grape/dsl/settings_spec.rb +2 -0
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +53 -523
- data/spec/grape/entity_spec.rb +9 -1
- data/spec/grape/exceptions/base_spec.rb +67 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
- data/spec/grape/exceptions/missing_option_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
- data/spec/grape/exceptions/validation_spec.rb +3 -1
- data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
- data/spec/grape/integration/rack_spec.rb +25 -7
- data/spec/grape/loading_spec.rb +2 -0
- data/spec/grape/middleware/auth/base_spec.rb +2 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +10 -0
- data/spec/grape/middleware/error_spec.rb +3 -1
- data/spec/grape/middleware/exception_spec.rb +4 -2
- data/spec/grape/middleware/formatter_spec.rb +33 -16
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +12 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +9 -1
- data/spec/grape/middleware/versioner/param_spec.rb +3 -1
- data/spec/grape/middleware/versioner/path_spec.rb +3 -1
- data/spec/grape/middleware/versioner_spec.rb +2 -0
- data/spec/grape/named_api_spec.rb +21 -0
- data/spec/grape/parser_spec.rb +7 -5
- data/spec/grape/path_spec.rb +6 -4
- data/spec/grape/presenters/presenter_spec.rb +2 -0
- data/spec/grape/request_spec.rb +26 -0
- data/spec/grape/util/inheritable_setting_spec.rb +2 -0
- data/spec/grape/util/inheritable_values_spec.rb +2 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
- data/spec/grape/util/stackable_values_spec.rb +3 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
- data/spec/grape/validations/params_scope_spec.rb +213 -9
- data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +9 -36
- data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
- data/spec/grape/validations/validators/coerce_spec.rb +476 -135
- data/spec/grape/validations/validators/default_spec.rb +172 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
- data/spec/grape/validations/validators/except_values_spec.rb +4 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
- data/spec/grape/validations/validators/presence_spec.rb +30 -0
- data/spec/grape/validations/validators/regexp_spec.rb +2 -0
- data/spec/grape/validations/validators/same_as_spec.rb +65 -0
- data/spec/grape/validations/validators/values_spec.rb +30 -5
- data/spec/grape/validations_spec.rb +388 -50
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -0
- data/spec/integration/multi_xml/xml_spec.rb +2 -0
- data/spec/shared/versioning_examples.rb +22 -20
- data/spec/spec_helper.rb +12 -1
- data/spec/support/basic_auth_encode_helpers.rb +2 -0
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +8 -8
- metadata +86 -48
- 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/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
data/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2010-
|
|
1
|
+
Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
4
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/grape)
|
|
4
|
-
[](https://gemnasium.com/ruby-grape/grape)
|
|
4
|
+
[](https://github.com/ruby-grape/grape/actions)
|
|
6
5
|
[](https://codeclimate.com/github/ruby-grape/grape)
|
|
7
6
|
[](https://coveralls.io/github/ruby-grape/grape?branch=master)
|
|
8
|
-
[](https://git.legal/projects/1364)
|
|
7
|
+
[](https://inch-ci.org/github/ruby-grape/grape)
|
|
10
8
|
[](https://gitter.im/ruby-grape/grape?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
11
9
|
|
|
12
10
|
## Table of Contents
|
|
@@ -14,20 +12,29 @@
|
|
|
14
12
|
- [What is Grape?](#what-is-grape)
|
|
15
13
|
- [Stable Release](#stable-release)
|
|
16
14
|
- [Project Resources](#project-resources)
|
|
15
|
+
- [Grape for Enterprise](#grape-for-enterprise)
|
|
17
16
|
- [Installation](#installation)
|
|
18
17
|
- [Basic Usage](#basic-usage)
|
|
19
18
|
- [Mounting](#mounting)
|
|
19
|
+
- [All](#all)
|
|
20
20
|
- [Rack](#rack)
|
|
21
21
|
- [ActiveRecord without Rails](#activerecord-without-rails)
|
|
22
|
+
- [Rails 4](#rails-4)
|
|
23
|
+
- [Rails 5+](#rails-5)
|
|
22
24
|
- [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
|
|
23
25
|
- [Rails](#rails)
|
|
26
|
+
- [Rails < 5.2](#rails--52)
|
|
27
|
+
- [Rails 6.0](#rails-60)
|
|
24
28
|
- [Modules](#modules)
|
|
29
|
+
- [Remounting](#remounting)
|
|
30
|
+
- [Mount Configuration](#mount-configuration)
|
|
25
31
|
- [Versioning](#versioning)
|
|
26
32
|
- [Path](#path)
|
|
27
33
|
- [Header](#header)
|
|
28
34
|
- [Accept-Version Header](#accept-version-header)
|
|
29
35
|
- [Param](#param)
|
|
30
36
|
- [Describing Methods](#describing-methods)
|
|
37
|
+
- [Configuration](#configuration)
|
|
31
38
|
- [Parameters](#parameters)
|
|
32
39
|
- [Params Class](#params-class)
|
|
33
40
|
- [Declared](#declared)
|
|
@@ -38,38 +45,43 @@
|
|
|
38
45
|
- [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
|
|
39
46
|
- [Custom Types and Coercions](#custom-types-and-coercions)
|
|
40
47
|
- [Multipart File Parameters](#multipart-file-parameters)
|
|
41
|
-
- [First-Class
|
|
48
|
+
- [First-Class JSON Types](#first-class-json-types)
|
|
42
49
|
- [Multiple Allowed Types](#multiple-allowed-types)
|
|
43
50
|
- [Validation of Nested Parameters](#validation-of-nested-parameters)
|
|
44
51
|
- [Dependent Parameters](#dependent-parameters)
|
|
45
52
|
- [Group Options](#group-options)
|
|
46
|
-
- [
|
|
53
|
+
- [Renaming](#renaming)
|
|
47
54
|
- [Built-in Validators](#built-in-validators)
|
|
48
|
-
- [
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
51
|
-
- [
|
|
52
|
-
- [
|
|
53
|
-
- [
|
|
54
|
-
- [
|
|
55
|
-
- [
|
|
56
|
-
- [
|
|
55
|
+
- [allow_blank](#allow_blank)
|
|
56
|
+
- [values](#values)
|
|
57
|
+
- [except_values](#except_values)
|
|
58
|
+
- [same_as](#same_as)
|
|
59
|
+
- [regexp](#regexp)
|
|
60
|
+
- [mutually_exclusive](#mutually_exclusive)
|
|
61
|
+
- [exactly_one_of](#exactly_one_of)
|
|
62
|
+
- [at_least_one_of](#at_least_one_of)
|
|
63
|
+
- [all_or_none_of](#all_or_none_of)
|
|
64
|
+
- [Nested mutually_exclusive, exactly_one_of, at_least_one_of, all_or_none_of](#nested-mutually_exclusive-exactly_one_of-at_least_one_of-all_or_none_of)
|
|
57
65
|
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
|
|
58
66
|
- [Custom Validators](#custom-validators)
|
|
59
67
|
- [Validation Errors](#validation-errors)
|
|
60
68
|
- [I18n](#i18n)
|
|
61
69
|
- [Custom Validation messages](#custom-validation-messages)
|
|
62
|
-
- [
|
|
63
|
-
- [
|
|
64
|
-
- [
|
|
65
|
-
- [
|
|
66
|
-
- [
|
|
67
|
-
- [
|
|
68
|
-
- [
|
|
69
|
-
- [
|
|
70
|
+
- [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
|
|
71
|
+
- [same_as](#same_as-1)
|
|
72
|
+
- [all_or_none_of](#all_or_none_of-1)
|
|
73
|
+
- [mutually_exclusive](#mutually_exclusive-1)
|
|
74
|
+
- [exactly_one_of](#exactly_one_of-1)
|
|
75
|
+
- [at_least_one_of](#at_least_one_of-1)
|
|
76
|
+
- [Coerce](#coerce)
|
|
77
|
+
- [With Lambdas](#with-lambdas)
|
|
78
|
+
- [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
|
|
70
79
|
- [Overriding Attribute Names](#overriding-attribute-names)
|
|
71
80
|
- [With Default](#with-default)
|
|
72
81
|
- [Headers](#headers)
|
|
82
|
+
- [Request](#request)
|
|
83
|
+
- [Header Case Handling](#header-case-handling)
|
|
84
|
+
- [Response](#response)
|
|
73
85
|
- [Routes](#routes)
|
|
74
86
|
- [Helpers](#helpers)
|
|
75
87
|
- [Path Helpers](#path-helpers)
|
|
@@ -105,7 +117,7 @@
|
|
|
105
117
|
- [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
|
|
106
118
|
- [Describing and Inspecting an API](#describing-and-inspecting-an-api)
|
|
107
119
|
- [Current Route and Endpoint](#current-route-and-endpoint)
|
|
108
|
-
- [Before and
|
|
120
|
+
- [Before, After and Finally](#before-after-and-finally)
|
|
109
121
|
- [Anchoring](#anchoring)
|
|
110
122
|
- [Using Custom Middleware](#using-custom-middleware)
|
|
111
123
|
- [Grape Middleware](#grape-middleware)
|
|
@@ -132,6 +144,7 @@
|
|
|
132
144
|
- [format_response.grape](#format_responsegrape)
|
|
133
145
|
- [Monitoring Products](#monitoring-products)
|
|
134
146
|
- [Contributing to Grape](#contributing-to-grape)
|
|
147
|
+
- [Security](#security)
|
|
135
148
|
- [License](#license)
|
|
136
149
|
- [Copyright](#copyright)
|
|
137
150
|
|
|
@@ -145,7 +158,7 @@ content negotiation, versioning and much more.
|
|
|
145
158
|
|
|
146
159
|
## Stable Release
|
|
147
160
|
|
|
148
|
-
You're reading the documentation for the stable release of Grape, **
|
|
161
|
+
You're reading the documentation for the stable release of Grape, **v1.5.3**.
|
|
149
162
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
|
150
163
|
|
|
151
164
|
## Project Resources
|
|
@@ -155,8 +168,18 @@ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
|
|
155
168
|
* Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
|
|
156
169
|
* [Follow us on Twitter](https://twitter.com/grapeframework)
|
|
157
170
|
|
|
171
|
+
## Grape for Enterprise
|
|
172
|
+
|
|
173
|
+
Available as part of the Tidelift Subscription.
|
|
174
|
+
|
|
175
|
+
The maintainers of Grape are working with Tidelift to deliver commercial support and maintenance. Save time, reduce risk, and improve code health, while paying the maintainers of Grape. Click [here](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) for more details.
|
|
176
|
+
|
|
177
|
+
In 2020, we plan to use the money towards gathering Grape contributors for dinner in New York City.
|
|
178
|
+
|
|
158
179
|
## Installation
|
|
159
180
|
|
|
181
|
+
Ruby 2.4 or newer is required.
|
|
182
|
+
|
|
160
183
|
Grape is available as a gem, to install it just install the gem:
|
|
161
184
|
|
|
162
185
|
gem install grape
|
|
@@ -204,7 +227,7 @@ module Twitter
|
|
|
204
227
|
|
|
205
228
|
desc 'Return a status.'
|
|
206
229
|
params do
|
|
207
|
-
requires :id, type: Integer, desc: 'Status
|
|
230
|
+
requires :id, type: Integer, desc: 'Status ID.'
|
|
208
231
|
end
|
|
209
232
|
route_param :id do
|
|
210
233
|
get do
|
|
@@ -252,6 +275,17 @@ end
|
|
|
252
275
|
|
|
253
276
|
## Mounting
|
|
254
277
|
|
|
278
|
+
### All
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
Twitter::API.compile!
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
This can be added to your `config.ru` (if using rackup), `application.rb` (if using rails), or any file that loads your server.
|
|
288
|
+
|
|
255
289
|
### Rack
|
|
256
290
|
|
|
257
291
|
The above sample creates a Rack application that can be run from a rackup `config.ru` file
|
|
@@ -261,6 +295,13 @@ with `rackup`:
|
|
|
261
295
|
run Twitter::API
|
|
262
296
|
```
|
|
263
297
|
|
|
298
|
+
(With pre-loading you can use)
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
Twitter::API.compile!
|
|
302
|
+
run Twitter::API
|
|
303
|
+
```
|
|
304
|
+
|
|
264
305
|
And would respond to the following routes:
|
|
265
306
|
|
|
266
307
|
GET /api/statuses/public_timeline
|
|
@@ -277,13 +318,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
|
|
|
277
318
|
If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
|
|
278
319
|
is handled correctly.
|
|
279
320
|
|
|
321
|
+
#### Rails 4
|
|
322
|
+
|
|
280
323
|
The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
|
|
281
324
|
`config.ru` before mounting Grape, e.g.:
|
|
282
325
|
|
|
283
326
|
```ruby
|
|
284
327
|
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
|
328
|
+
```
|
|
285
329
|
|
|
286
|
-
|
|
330
|
+
#### Rails 5+
|
|
331
|
+
|
|
332
|
+
Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
use OTR::ActiveRecord::ConnectionManagement
|
|
287
336
|
```
|
|
288
337
|
|
|
289
338
|
### Alongside Sinatra (or other frameworks)
|
|
@@ -310,13 +359,24 @@ class Web < Sinatra::Base
|
|
|
310
359
|
end
|
|
311
360
|
|
|
312
361
|
use Rack::Session::Cookie
|
|
313
|
-
run Rack::Cascade.new [
|
|
362
|
+
run Rack::Cascade.new [Web, API]
|
|
314
363
|
```
|
|
315
364
|
|
|
365
|
+
Note that order of loading apps using `Rack::Cascade` matters. The grape application must be last if you want to raise custom 404 errors from grape (such as `error!('Not Found',404)`). If the grape application is not last and returns 404 or 405 response, [cascade utilizes that as a signal to try the next app](https://www.rubydoc.info/gems/rack/Rack/Cascade). This may lead to undesirable behavior showing the [wrong 404 page from the wrong app](https://github.com/ruby-grape/grape/issues/1515).
|
|
366
|
+
|
|
367
|
+
|
|
316
368
|
### Rails
|
|
317
369
|
|
|
318
370
|
Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
|
|
319
371
|
|
|
372
|
+
Modify `config/routes`:
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
mount Twitter::API => '/'
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### Rails < 5.2
|
|
379
|
+
|
|
320
380
|
Modify `application.rb`:
|
|
321
381
|
|
|
322
382
|
```ruby
|
|
@@ -324,14 +384,18 @@ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
|
|
|
324
384
|
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
|
325
385
|
```
|
|
326
386
|
|
|
327
|
-
|
|
387
|
+
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
|
388
|
+
|
|
389
|
+
#### Rails 6.0
|
|
390
|
+
|
|
391
|
+
For Rails versions greater than 6.0.0.beta2, `Zeitwerk` autoloader is the default for CRuby. By default `Zeitwerk` inflects `api` as `Api` instead of `API`. To make our example work, you need to uncomment the lines at the bottom of `config/initializers/inflections.rb`, and add `API` as an acronym:
|
|
328
392
|
|
|
329
393
|
```ruby
|
|
330
|
-
|
|
394
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|
395
|
+
inflect.acronym 'API'
|
|
396
|
+
end
|
|
331
397
|
```
|
|
332
398
|
|
|
333
|
-
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
|
334
|
-
|
|
335
399
|
### Modules
|
|
336
400
|
|
|
337
401
|
You can mount multiple API implementations inside another one. These don't have to be
|
|
@@ -365,6 +429,131 @@ class Twitter::API < Grape::API
|
|
|
365
429
|
end
|
|
366
430
|
```
|
|
367
431
|
|
|
432
|
+
## Remounting
|
|
433
|
+
|
|
434
|
+
You can mount the same endpoints in two different locations.
|
|
435
|
+
|
|
436
|
+
```ruby
|
|
437
|
+
class Voting::API < Grape::API
|
|
438
|
+
namespace 'votes' do
|
|
439
|
+
get do
|
|
440
|
+
# Your logic
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
post do
|
|
444
|
+
# Your logic
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
class Post::API < Grape::API
|
|
450
|
+
mount Voting::API
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
class Comment::API < Grape::API
|
|
454
|
+
mount Voting::API
|
|
455
|
+
end
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Assuming that the post and comment endpoints are mounted in `/posts` and `/comments`, you should now be able to do `get /posts/votes`, `post /posts/votes`, `get /comments/votes` and `post /comments/votes`.
|
|
459
|
+
|
|
460
|
+
### Mount Configuration
|
|
461
|
+
|
|
462
|
+
You can configure remountable endpoints to change how they behave according to where they are mounted.
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
class Voting::API < Grape::API
|
|
466
|
+
namespace 'votes' do
|
|
467
|
+
desc "Vote for your #{configuration[:votable]}"
|
|
468
|
+
get do
|
|
469
|
+
# Your logic
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
class Post::API < Grape::API
|
|
475
|
+
mount Voting::API, with: { votable: 'posts' }
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
class Comment::API < Grape::API
|
|
479
|
+
mount Voting::API, with: { votable: 'comments' }
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Note that if you're passing a hash as the first parameter to `mount`, you will need to explicitly put `()` around parameters:
|
|
484
|
+
```ruby
|
|
485
|
+
# good
|
|
486
|
+
mount({ ::Some::Api => '/some/api' }, with: { condition: true })
|
|
487
|
+
|
|
488
|
+
# bad
|
|
489
|
+
mount ::Some::Api => '/some/api', with: { condition: true }
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
|
|
493
|
+
|
|
494
|
+
If you want logic happening given on an `configuration`, you can use the helper `given`.
|
|
495
|
+
|
|
496
|
+
```ruby
|
|
497
|
+
class ConditionalEndpoint::API < Grape::API
|
|
498
|
+
given configuration[:some_setting] do
|
|
499
|
+
get 'mount_this_endpoint_conditionally' do
|
|
500
|
+
configuration[:configurable_response]
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
If you want a block of logic running every time an endpoint is mounted (within which you can access the `configuration` Hash)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
```ruby
|
|
510
|
+
class ConditionalEndpoint::API < Grape::API
|
|
511
|
+
mounted do
|
|
512
|
+
YourLogger.info "This API was mounted at: #{Time.now}"
|
|
513
|
+
|
|
514
|
+
get configuration[:endpoint_name] do
|
|
515
|
+
configuration[:configurable_response]
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
More complex results can be achieved by using `mounted` as an expression within which the `configuration` is already evaluated as a Hash.
|
|
522
|
+
|
|
523
|
+
```ruby
|
|
524
|
+
class ExpressionEndpointAPI < Grape::API
|
|
525
|
+
get(mounted { configuration[:route_name] || 'default_name' }) do
|
|
526
|
+
# some logic
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
```ruby
|
|
532
|
+
class BasicAPI < Grape::API
|
|
533
|
+
desc 'Statuses index' do
|
|
534
|
+
params: mounted { configuration[:entity] || API::Entities::Status }.documentation
|
|
535
|
+
end
|
|
536
|
+
params do
|
|
537
|
+
requires :all, using: mounted { configuration[:entity] || API::Entities::Status }.documentation
|
|
538
|
+
end
|
|
539
|
+
get '/statuses' do
|
|
540
|
+
statuses = Status.all
|
|
541
|
+
type = current_user.admin? ? :full : :default
|
|
542
|
+
present statuses, with: mounted { configuration[:entity] || API::Entities::Status }, type: type
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
class V1 < Grape::API
|
|
547
|
+
version 'v1'
|
|
548
|
+
mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Enitities::Status } }
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
class V2 < Grape::API
|
|
552
|
+
version 'v2'
|
|
553
|
+
mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Enitities::V2::Status } }
|
|
554
|
+
end
|
|
555
|
+
```
|
|
556
|
+
|
|
368
557
|
## Versioning
|
|
369
558
|
|
|
370
559
|
There are four strategies in which clients can reach your API's endpoints: `:path`,
|
|
@@ -445,10 +634,13 @@ version 'v1', using: :param, parameter: 'v'
|
|
|
445
634
|
|
|
446
635
|
## Describing Methods
|
|
447
636
|
|
|
448
|
-
You can add a description to API methods and namespaces.
|
|
637
|
+
You can add a description to API methods and namespaces. The description would be used by [grape-swagger][grape-swagger] to generate swagger compliant documentation.
|
|
638
|
+
|
|
639
|
+
Note: Description block is only for documentation and won't affects API behavior.
|
|
449
640
|
|
|
450
641
|
```ruby
|
|
451
642
|
desc 'Returns your public timeline.' do
|
|
643
|
+
summary 'summary'
|
|
452
644
|
detail 'more details'
|
|
453
645
|
params API::Entities::Status.documentation
|
|
454
646
|
success API::Entities::Entity
|
|
@@ -462,7 +654,13 @@ desc 'Returns your public timeline.' do
|
|
|
462
654
|
description: 'Not really needed',
|
|
463
655
|
required: false
|
|
464
656
|
}
|
|
465
|
-
|
|
657
|
+
hidden false
|
|
658
|
+
deprecated false
|
|
659
|
+
is_array true
|
|
660
|
+
nickname 'nickname'
|
|
661
|
+
produces ['application/json']
|
|
662
|
+
consumes ['application/json']
|
|
663
|
+
tags ['tag1', 'tag2']
|
|
466
664
|
end
|
|
467
665
|
get :public_timeline do
|
|
468
666
|
Status.limit(20)
|
|
@@ -475,6 +673,43 @@ end
|
|
|
475
673
|
* `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities
|
|
476
674
|
* `named`: A helper to give a route a name and find it with this name in the documentation Hash
|
|
477
675
|
* `headers`: A definition of the used Headers
|
|
676
|
+
* Other options can be found in [grape-swagger][grape-swagger]
|
|
677
|
+
|
|
678
|
+
[grape-swagger]: https://github.com/ruby-grape/grape-swagger
|
|
679
|
+
|
|
680
|
+
## Configuration
|
|
681
|
+
|
|
682
|
+
Use `Grape.configure` to set up global settings at load time.
|
|
683
|
+
Currently the configurable settings are:
|
|
684
|
+
|
|
685
|
+
* `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
|
|
686
|
+
|
|
687
|
+
To change a setting value make sure that at some point during load time the following code runs
|
|
688
|
+
|
|
689
|
+
```ruby
|
|
690
|
+
Grape.configure do |config|
|
|
691
|
+
config.setting = value
|
|
692
|
+
end
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
For example, for the `param_builder`, the following code could run in an initializer:
|
|
696
|
+
|
|
697
|
+
```ruby
|
|
698
|
+
Grape.configure do |config|
|
|
699
|
+
config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
|
|
700
|
+
end
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
You can also configure a single API:
|
|
704
|
+
|
|
705
|
+
```ruby
|
|
706
|
+
API.configure do |config|
|
|
707
|
+
config[key] = value
|
|
708
|
+
end
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
This will be available inside the API with `configuration`, as if it were
|
|
712
|
+
[mount configuration](#mount-configuration).
|
|
478
713
|
|
|
479
714
|
## Parameters
|
|
480
715
|
|
|
@@ -553,13 +788,20 @@ params do
|
|
|
553
788
|
end
|
|
554
789
|
```
|
|
555
790
|
|
|
791
|
+
Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
|
|
792
|
+
|
|
556
793
|
In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
|
|
557
794
|
|
|
558
795
|
Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
|
|
559
796
|
|
|
560
797
|
### Declared
|
|
561
798
|
|
|
562
|
-
Grape allows you to access only the parameters that have been declared by your `params` block. It
|
|
799
|
+
Grape allows you to access only the parameters that have been declared by your `params` block. It will:
|
|
800
|
+
|
|
801
|
+
* Filter out the params that have been passed, but are not allowed.
|
|
802
|
+
* Include any optional params that are declared but not passed.
|
|
803
|
+
|
|
804
|
+
Consider the following API endpoint:
|
|
563
805
|
|
|
564
806
|
````ruby
|
|
565
807
|
format :json
|
|
@@ -592,9 +834,9 @@ Once we add parameters requirements, grape will start returning only the declare
|
|
|
592
834
|
format :json
|
|
593
835
|
|
|
594
836
|
params do
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
837
|
+
optional :user, type: Hash do
|
|
838
|
+
optional :first_name, type: String
|
|
839
|
+
optional :last_name, type: String
|
|
598
840
|
end
|
|
599
841
|
end
|
|
600
842
|
|
|
@@ -622,6 +864,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
|
622
864
|
}
|
|
623
865
|
````
|
|
624
866
|
|
|
867
|
+
Missing params that are declared as type `Hash` or `Array` will be included.
|
|
868
|
+
|
|
869
|
+
````ruby
|
|
870
|
+
format :json
|
|
871
|
+
|
|
872
|
+
params do
|
|
873
|
+
optional :user, type: Hash do
|
|
874
|
+
optional :first_name, type: String
|
|
875
|
+
optional :last_name, type: String
|
|
876
|
+
end
|
|
877
|
+
optional :widgets, type: Array
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
post 'users/signup' do
|
|
881
|
+
{ 'declared_params' => declared(params) }
|
|
882
|
+
end
|
|
883
|
+
````
|
|
884
|
+
|
|
885
|
+
**Request**
|
|
886
|
+
|
|
887
|
+
````bash
|
|
888
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
|
|
889
|
+
````
|
|
890
|
+
|
|
891
|
+
**Response**
|
|
892
|
+
|
|
893
|
+
````json
|
|
894
|
+
{
|
|
895
|
+
"declared_params": {
|
|
896
|
+
"user": {
|
|
897
|
+
"first_name": null,
|
|
898
|
+
"last_name": null
|
|
899
|
+
},
|
|
900
|
+
"widgets": []
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
````
|
|
904
|
+
|
|
625
905
|
The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
|
|
626
906
|
|
|
627
907
|
The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
|
|
@@ -680,8 +960,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
|
|
|
680
960
|
format :json
|
|
681
961
|
|
|
682
962
|
params do
|
|
683
|
-
requires :
|
|
684
|
-
|
|
963
|
+
requires :user, type: Hash do
|
|
964
|
+
requires :first_name, type: String
|
|
965
|
+
optional :last_name, type: String
|
|
966
|
+
end
|
|
685
967
|
end
|
|
686
968
|
|
|
687
969
|
post 'users/signup' do
|
|
@@ -834,13 +1116,13 @@ params do
|
|
|
834
1116
|
end
|
|
835
1117
|
```
|
|
836
1118
|
|
|
837
|
-
Note that default values will be passed through to any validation options specified.
|
|
838
|
-
The following example will always fail if `:color` is not explicitly provided.
|
|
839
|
-
|
|
840
1119
|
Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
|
|
841
1120
|
number for each call to the endpoint of this `params` block. To have the default evaluate
|
|
842
1121
|
lazily with each request use a lambda, like `:random_number` above.
|
|
843
1122
|
|
|
1123
|
+
Note that default values will be passed through to any validation options specified.
|
|
1124
|
+
The following example will always fail if `:color` is not explicitly provided.
|
|
1125
|
+
|
|
844
1126
|
```ruby
|
|
845
1127
|
params do
|
|
846
1128
|
optional :color, type: String, default: 'blue', values: ['red', 'green']
|
|
@@ -900,7 +1182,8 @@ Aside from the default set of supported types listed above, any class can be
|
|
|
900
1182
|
used as a type as long as an explicit coercion method is supplied. If the type
|
|
901
1183
|
implements a class-level `parse` method, Grape will use it automatically.
|
|
902
1184
|
This method must take one string argument and return an instance of the correct
|
|
903
|
-
type, or
|
|
1185
|
+
type, or return an instance of `Grape::Types::InvalidValue` which optionally
|
|
1186
|
+
accepts a message to be returned in the response.
|
|
904
1187
|
|
|
905
1188
|
```ruby
|
|
906
1189
|
class Color
|
|
@@ -910,8 +1193,9 @@ class Color
|
|
|
910
1193
|
end
|
|
911
1194
|
|
|
912
1195
|
def self.parse(value)
|
|
913
|
-
|
|
914
|
-
|
|
1196
|
+
return new(value) if %w[blue red green]).include?(value)
|
|
1197
|
+
|
|
1198
|
+
Grape::Types::InvalidValue.new('Unsupported color')
|
|
915
1199
|
end
|
|
916
1200
|
end
|
|
917
1201
|
|
|
@@ -934,7 +1218,7 @@ parameter, and the return value must match the given `type`.
|
|
|
934
1218
|
|
|
935
1219
|
```ruby
|
|
936
1220
|
params do
|
|
937
|
-
requires :passwd, type: String, coerce_with: Base64.method(:
|
|
1221
|
+
requires :passwd, type: String, coerce_with: Base64.method(:decode64)
|
|
938
1222
|
requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
|
|
939
1223
|
|
|
940
1224
|
requires :obj, type: Hash, coerce_with: JSON do
|
|
@@ -943,6 +1227,7 @@ params do
|
|
|
943
1227
|
end
|
|
944
1228
|
end
|
|
945
1229
|
```
|
|
1230
|
+
Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
|
|
946
1231
|
|
|
947
1232
|
Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
|
|
948
1233
|
It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
|
|
@@ -1109,6 +1394,18 @@ params do
|
|
|
1109
1394
|
end
|
|
1110
1395
|
```
|
|
1111
1396
|
|
|
1397
|
+
You can rename parameters:
|
|
1398
|
+
|
|
1399
|
+
```ruby
|
|
1400
|
+
params do
|
|
1401
|
+
optional :category, as: :type
|
|
1402
|
+
given type: ->(val) { val == 'foo' } do
|
|
1403
|
+
requires :description
|
|
1404
|
+
end
|
|
1405
|
+
end
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1408
|
+
Note: param in `given` should be the renamed one. In the example, it should be `type`, not `category`.
|
|
1112
1409
|
|
|
1113
1410
|
### Group Options
|
|
1114
1411
|
|
|
@@ -1137,9 +1434,9 @@ params do
|
|
|
1137
1434
|
end
|
|
1138
1435
|
```
|
|
1139
1436
|
|
|
1140
|
-
###
|
|
1437
|
+
### Renaming
|
|
1141
1438
|
|
|
1142
|
-
You can
|
|
1439
|
+
You can rename parameters using `as`, which can be useful when refactoring existing APIs:
|
|
1143
1440
|
|
|
1144
1441
|
```ruby
|
|
1145
1442
|
resource :users do
|
|
@@ -1248,6 +1545,17 @@ params do
|
|
|
1248
1545
|
end
|
|
1249
1546
|
```
|
|
1250
1547
|
|
|
1548
|
+
#### `same_as`
|
|
1549
|
+
|
|
1550
|
+
A `same_as` option can be given to ensure that values of parameters match.
|
|
1551
|
+
|
|
1552
|
+
```ruby
|
|
1553
|
+
params do
|
|
1554
|
+
requires :password
|
|
1555
|
+
requires :password_confirmation, same_as: :password
|
|
1556
|
+
end
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1251
1559
|
#### `regexp`
|
|
1252
1560
|
|
|
1253
1561
|
Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
|
|
@@ -1485,7 +1793,7 @@ params do
|
|
|
1485
1793
|
end
|
|
1486
1794
|
```
|
|
1487
1795
|
|
|
1488
|
-
Every validation will have
|
|
1796
|
+
Every validation will have its own instance of the validator, which means that the validator can have a state.
|
|
1489
1797
|
|
|
1490
1798
|
### Validation Errors
|
|
1491
1799
|
|
|
@@ -1546,6 +1854,8 @@ end
|
|
|
1546
1854
|
Grape supports I18n for parameter-related error messages, but will fallback to English if
|
|
1547
1855
|
translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
|
|
1548
1856
|
|
|
1857
|
+
In case your app enforces available locales only and :en is not included in your available locales, Grape cannot fall back to English and will return the translation key for the error message. To avoid this behaviour, either provide a translation for your default locale or add :en to your available locales.
|
|
1858
|
+
|
|
1549
1859
|
### Custom Validation messages
|
|
1550
1860
|
|
|
1551
1861
|
Grape supports custom validation messages for parameter-related and coerce-related error messages.
|
|
@@ -1558,6 +1868,15 @@ params do
|
|
|
1558
1868
|
end
|
|
1559
1869
|
```
|
|
1560
1870
|
|
|
1871
|
+
#### `same_as`
|
|
1872
|
+
|
|
1873
|
+
```ruby
|
|
1874
|
+
params do
|
|
1875
|
+
requires :password
|
|
1876
|
+
requires :password_confirmation, same_as: { value: :password, message: 'not match' }
|
|
1877
|
+
end
|
|
1878
|
+
```
|
|
1879
|
+
|
|
1561
1880
|
#### `all_or_none_of`
|
|
1562
1881
|
|
|
1563
1882
|
```ruby
|
|
@@ -1587,7 +1906,7 @@ params do
|
|
|
1587
1906
|
optional :beer
|
|
1588
1907
|
optional :wine
|
|
1589
1908
|
optional :juice
|
|
1590
|
-
exactly_one_of :beer, :wine, :juice, message: {exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required"}
|
|
1909
|
+
exactly_one_of :beer, :wine, :juice, message: { exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required" }
|
|
1591
1910
|
end
|
|
1592
1911
|
```
|
|
1593
1912
|
|
|
@@ -1606,7 +1925,7 @@ end
|
|
|
1606
1925
|
|
|
1607
1926
|
```ruby
|
|
1608
1927
|
params do
|
|
1609
|
-
requires :int, type: {value: Integer, message: "type cast is invalid" }
|
|
1928
|
+
requires :int, type: { value: Integer, message: "type cast is invalid" }
|
|
1610
1929
|
end
|
|
1611
1930
|
```
|
|
1612
1931
|
|
|
@@ -1668,6 +1987,7 @@ end
|
|
|
1668
1987
|
|
|
1669
1988
|
## Headers
|
|
1670
1989
|
|
|
1990
|
+
### Request
|
|
1671
1991
|
Request headers are available through the `headers` helper or from `env` in their original form.
|
|
1672
1992
|
|
|
1673
1993
|
```ruby
|
|
@@ -1682,13 +2002,30 @@ get do
|
|
|
1682
2002
|
end
|
|
1683
2003
|
```
|
|
1684
2004
|
|
|
2005
|
+
#### Header Case Handling
|
|
2006
|
+
|
|
2007
|
+
The above example may have been requested as follows:
|
|
2008
|
+
|
|
2009
|
+
``` shell
|
|
2010
|
+
curl -H "secret_PassWord: swordfish" ...
|
|
2011
|
+
```
|
|
2012
|
+
|
|
2013
|
+
The header name will have been normalized for you.
|
|
2014
|
+
|
|
2015
|
+
- In the `header` helper names will be coerced into a capitalized kebab case.
|
|
2016
|
+
- In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_'.
|
|
2017
|
+
|
|
2018
|
+
The header name will have been normalized per HTTP standards defined in [RFC2616 Section 4.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) regardless of what is being sent by a client.
|
|
2019
|
+
|
|
2020
|
+
### Response
|
|
2021
|
+
|
|
1685
2022
|
You can set a response header with `header` inside an API.
|
|
1686
2023
|
|
|
1687
2024
|
```ruby
|
|
1688
2025
|
header 'X-Robots-Tag', 'noindex'
|
|
1689
2026
|
```
|
|
1690
2027
|
|
|
1691
|
-
When raising `error!`, pass additional headers as arguments.
|
|
2028
|
+
When raising `error!`, pass additional headers as arguments. Additional headers will be merged with headers set before `error!` call.
|
|
1692
2029
|
|
|
1693
2030
|
```ruby
|
|
1694
2031
|
error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
|
|
@@ -1696,6 +2033,56 @@ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
|
|
|
1696
2033
|
|
|
1697
2034
|
## Routes
|
|
1698
2035
|
|
|
2036
|
+
To define routes you can use the `route` method or the shorthands for the HTTP verbs. To define a route that accepts any route set to `:any`.
|
|
2037
|
+
Parts of the path that are denoted with a colon will be interpreted as route parameters.
|
|
2038
|
+
|
|
2039
|
+
```ruby
|
|
2040
|
+
route :get, 'status' do
|
|
2041
|
+
end
|
|
2042
|
+
|
|
2043
|
+
# is the same as
|
|
2044
|
+
|
|
2045
|
+
get 'status' do
|
|
2046
|
+
end
|
|
2047
|
+
|
|
2048
|
+
# is the same as
|
|
2049
|
+
|
|
2050
|
+
get :status do
|
|
2051
|
+
end
|
|
2052
|
+
|
|
2053
|
+
# is NOT the same as
|
|
2054
|
+
|
|
2055
|
+
get ':status' do # this makes params[:status] available
|
|
2056
|
+
end
|
|
2057
|
+
|
|
2058
|
+
# This will make both params[:status_id] and params[:id] available
|
|
2059
|
+
|
|
2060
|
+
get 'statuses/:status_id/reviews/:id' do
|
|
2061
|
+
end
|
|
2062
|
+
```
|
|
2063
|
+
|
|
2064
|
+
To declare a namespace that prefixes all routes within, use the `namespace` method. `group`, `resource`, `resources` and `segment` are aliases to this method. Any endpoints within will share their parent context as well as any configuration done in the namespace context.
|
|
2065
|
+
|
|
2066
|
+
The `route_param` method is a convenient method for defining a parameter route segment. If you define a type, it will add a validation for this parameter.
|
|
2067
|
+
|
|
2068
|
+
```ruby
|
|
2069
|
+
route_param :id, type: Integer do
|
|
2070
|
+
get 'status' do
|
|
2071
|
+
end
|
|
2072
|
+
end
|
|
2073
|
+
|
|
2074
|
+
# is the same as
|
|
2075
|
+
|
|
2076
|
+
namespace ':id' do
|
|
2077
|
+
params do
|
|
2078
|
+
requires :id, type: Integer
|
|
2079
|
+
end
|
|
2080
|
+
|
|
2081
|
+
get 'status' do
|
|
2082
|
+
end
|
|
2083
|
+
end
|
|
2084
|
+
```
|
|
2085
|
+
|
|
1699
2086
|
Optionally, you can define requirements for your named route parameters using regular
|
|
1700
2087
|
expressions on namespace or endpoint. The route will match only if all requirements are met.
|
|
1701
2088
|
|
|
@@ -2028,6 +2415,12 @@ instead of a message.
|
|
|
2028
2415
|
error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
|
|
2029
2416
|
```
|
|
2030
2417
|
|
|
2418
|
+
You can set additional headers for the response. They will be merged with headers set before `error!` call.
|
|
2419
|
+
|
|
2420
|
+
```ruby
|
|
2421
|
+
error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
|
|
2422
|
+
```
|
|
2423
|
+
|
|
2031
2424
|
You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
|
|
2032
2425
|
|
|
2033
2426
|
```ruby
|
|
@@ -2182,7 +2575,7 @@ You can also rescue all exceptions with a code block and handle the Rack respons
|
|
|
2182
2575
|
```ruby
|
|
2183
2576
|
class Twitter::API < Grape::API
|
|
2184
2577
|
rescue_from :all do |e|
|
|
2185
|
-
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
|
|
2578
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
|
|
2186
2579
|
end
|
|
2187
2580
|
end
|
|
2188
2581
|
```
|
|
@@ -2253,9 +2646,9 @@ class Twitter::API < Grape::API
|
|
|
2253
2646
|
end
|
|
2254
2647
|
```
|
|
2255
2648
|
|
|
2256
|
-
The `rescue_from`
|
|
2649
|
+
The `rescue_from` handler must return a `Rack::Response` object, call `error!`, or raise an exception (either the original exception or another custom one). The exception raised in `rescue_from` will be handled outside Grape. For example, if you mount Grape in Rails, the exception will be handle by [Rails Action Controller](https://guides.rubyonrails.org/action_controller_overview.html#rescue).
|
|
2257
2650
|
|
|
2258
|
-
|
|
2651
|
+
Alternately, use the `with` option in `rescue_from` to specify a method or a `proc`.
|
|
2259
2652
|
|
|
2260
2653
|
```ruby
|
|
2261
2654
|
class Twitter::API < Grape::API
|
|
@@ -2271,6 +2664,17 @@ class Twitter::API < Grape::API
|
|
|
2271
2664
|
end
|
|
2272
2665
|
```
|
|
2273
2666
|
|
|
2667
|
+
Inside the `rescue_from` block, the environment of the original controller method(`.self` receiver) is accessible through the `#context` method.
|
|
2668
|
+
|
|
2669
|
+
```ruby
|
|
2670
|
+
class Twitter::API < Grape::API
|
|
2671
|
+
rescue_from :all do |e|
|
|
2672
|
+
user_id = context.params[:user_id]
|
|
2673
|
+
error!("error for #{user_id}")
|
|
2674
|
+
end
|
|
2675
|
+
end
|
|
2676
|
+
```
|
|
2677
|
+
|
|
2274
2678
|
#### Rescuing exceptions inside namespaces
|
|
2275
2679
|
|
|
2276
2680
|
You could put `rescue_from` clauses inside a namespace and they will take precedence over ones
|
|
@@ -2293,7 +2697,7 @@ class Twitter::API < Grape::API
|
|
|
2293
2697
|
end
|
|
2294
2698
|
```
|
|
2295
2699
|
|
|
2296
|
-
Here `'inner'` will be result of handling
|
|
2700
|
+
Here `'inner'` will be result of handling occurred `ArgumentError`.
|
|
2297
2701
|
|
|
2298
2702
|
#### Unrescuable Exceptions
|
|
2299
2703
|
|
|
@@ -2824,17 +3228,19 @@ end
|
|
|
2824
3228
|
|
|
2825
3229
|
Use `body false` to return `204 No Content` without any data or content-type.
|
|
2826
3230
|
|
|
2827
|
-
You can also set the response to a file with `
|
|
3231
|
+
You can also set the response to a file with `sendfile`. This works with the
|
|
3232
|
+
[Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
|
|
3233
|
+
the file through your web server software.
|
|
2828
3234
|
|
|
2829
3235
|
```ruby
|
|
2830
3236
|
class API < Grape::API
|
|
2831
3237
|
get '/' do
|
|
2832
|
-
|
|
3238
|
+
sendfile '/path/to/file'
|
|
2833
3239
|
end
|
|
2834
3240
|
end
|
|
2835
3241
|
```
|
|
2836
3242
|
|
|
2837
|
-
|
|
3243
|
+
To stream a file in chunks use `stream`
|
|
2838
3244
|
|
|
2839
3245
|
```ruby
|
|
2840
3246
|
class API < Grape::API
|
|
@@ -2844,6 +3250,26 @@ class API < Grape::API
|
|
|
2844
3250
|
end
|
|
2845
3251
|
```
|
|
2846
3252
|
|
|
3253
|
+
If you want to stream non-file data use the `stream` method and a `Stream` object.
|
|
3254
|
+
This is an object that responds to `each` and yields for each chunk to send to the client.
|
|
3255
|
+
Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
|
|
3256
|
+
|
|
3257
|
+
```ruby
|
|
3258
|
+
class MyStream
|
|
3259
|
+
def each
|
|
3260
|
+
yield 'part 1'
|
|
3261
|
+
yield 'part 2'
|
|
3262
|
+
yield 'part 3'
|
|
3263
|
+
end
|
|
3264
|
+
end
|
|
3265
|
+
|
|
3266
|
+
class API < Grape::API
|
|
3267
|
+
get '/' do
|
|
3268
|
+
stream MyStream.new
|
|
3269
|
+
end
|
|
3270
|
+
end
|
|
3271
|
+
```
|
|
3272
|
+
|
|
2847
3273
|
## Authentication
|
|
2848
3274
|
|
|
2849
3275
|
### Basic and Digest Auth
|
|
@@ -2855,14 +3281,13 @@ applies to the current namespace and any children, but not parents.
|
|
|
2855
3281
|
```ruby
|
|
2856
3282
|
http_basic do |username, password|
|
|
2857
3283
|
# verify user's password here
|
|
2858
|
-
|
|
3284
|
+
# IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
|
|
2859
3285
|
end
|
|
2860
3286
|
```
|
|
2861
3287
|
|
|
2862
3288
|
```ruby
|
|
2863
3289
|
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
|
2864
3290
|
# lookup the user's password here
|
|
2865
|
-
{ 'user1' => 'password1' }[username]
|
|
2866
3291
|
end
|
|
2867
3292
|
```
|
|
2868
3293
|
|
|
@@ -2893,7 +3318,9 @@ end
|
|
|
2893
3318
|
|
|
2894
3319
|
```
|
|
2895
3320
|
|
|
2896
|
-
Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
|
|
3321
|
+
Use [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
|
|
3322
|
+
|
|
3323
|
+
You can access the controller params, headers, and helpers through the context with the `#context` method inside any auth middleware inherited from `Grape::Middleware::Auth::Base`.
|
|
2897
3324
|
|
|
2898
3325
|
## Describing and Inspecting an API
|
|
2899
3326
|
|
|
@@ -2962,19 +3389,22 @@ class ApiLogger < Grape::Middleware::Base
|
|
|
2962
3389
|
end
|
|
2963
3390
|
```
|
|
2964
3391
|
|
|
2965
|
-
## Before and
|
|
3392
|
+
## Before, After and Finally
|
|
2966
3393
|
|
|
2967
3394
|
Blocks can be executed before or after every API call, using `before`, `after`,
|
|
2968
3395
|
`before_validation` and `after_validation`.
|
|
3396
|
+
If the API fails the `after` call will not be triggered, if you need code to execute for sure
|
|
3397
|
+
use the `finally`.
|
|
2969
3398
|
|
|
2970
3399
|
Before and after callbacks execute in the following order:
|
|
2971
3400
|
|
|
2972
3401
|
1. `before`
|
|
2973
3402
|
2. `before_validation`
|
|
2974
3403
|
3. _validations_
|
|
2975
|
-
4. `after_validation`
|
|
2976
|
-
5. _the API call_
|
|
2977
|
-
6. `after`
|
|
3404
|
+
4. `after_validation` (upon successful validation)
|
|
3405
|
+
5. _the API call_ (upon successful validation)
|
|
3406
|
+
6. `after` (upon successful validation and API call)
|
|
3407
|
+
7. `finally` (always)
|
|
2978
3408
|
|
|
2979
3409
|
Steps 4, 5 and 6 only happen if validation succeeds.
|
|
2980
3410
|
|
|
@@ -2994,6 +3424,14 @@ before do
|
|
|
2994
3424
|
end
|
|
2995
3425
|
```
|
|
2996
3426
|
|
|
3427
|
+
You can ensure a block of code runs after every request (including failures) with `finally`:
|
|
3428
|
+
|
|
3429
|
+
```ruby
|
|
3430
|
+
finally do
|
|
3431
|
+
# this code will run after every request (successful or failed)
|
|
3432
|
+
end
|
|
3433
|
+
```
|
|
3434
|
+
|
|
2997
3435
|
**Namespaces**
|
|
2998
3436
|
|
|
2999
3437
|
Callbacks apply to each API call within and below the current namespace:
|
|
@@ -3196,6 +3634,8 @@ class API < Grape::API
|
|
|
3196
3634
|
end
|
|
3197
3635
|
```
|
|
3198
3636
|
|
|
3637
|
+
You can access the controller params, headers, and helpers through the context with the `#context` method inside any middleware inherited from `Grape::Middleware::Base`.
|
|
3638
|
+
|
|
3199
3639
|
### Rails Middleware
|
|
3200
3640
|
|
|
3201
3641
|
Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
|
|
@@ -3504,6 +3944,7 @@ Grape integrates with following third-party tools:
|
|
|
3504
3944
|
* **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
|
|
3505
3945
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
|
3506
3946
|
* **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
|
|
3947
|
+
* **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
|
|
3507
3948
|
|
|
3508
3949
|
## Contributing to Grape
|
|
3509
3950
|
|
|
@@ -3512,10 +3953,14 @@ features and discuss issues.
|
|
|
3512
3953
|
|
|
3513
3954
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
|
3514
3955
|
|
|
3956
|
+
## Security
|
|
3957
|
+
|
|
3958
|
+
See [SECURITY](SECURITY.md) for details.
|
|
3959
|
+
|
|
3515
3960
|
## License
|
|
3516
3961
|
|
|
3517
|
-
MIT License. See LICENSE for details.
|
|
3962
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
3518
3963
|
|
|
3519
3964
|
## Copyright
|
|
3520
3965
|
|
|
3521
|
-
Copyright (c) 2010-
|
|
3966
|
+
Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
|