grape 1.1.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +370 -44
- data/CONTRIBUTING.md +32 -1
- data/LICENSE +1 -1
- data/README.md +683 -87
- data/UPGRADING.md +481 -17
- data/grape.gemspec +15 -4
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +279 -0
- data/lib/grape/api.rb +144 -176
- data/lib/grape/config.rb +34 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +4 -0
- data/lib/grape/dry_types.rb +12 -0
- data/lib/grape/dsl/api.rb +1 -1
- data/lib/grape/dsl/callbacks.rb +21 -1
- data/lib/grape/dsl/configuration.rb +1 -1
- data/lib/grape/dsl/desc.rb +41 -23
- data/lib/grape/dsl/headers.rb +7 -2
- data/lib/grape/dsl/helpers.rb +10 -7
- data/lib/grape/dsl/inside_route.rb +118 -62
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +11 -4
- data/lib/grape/dsl/parameters.rb +33 -19
- data/lib/grape/dsl/request_response.rb +12 -9
- data/lib/grape/dsl/routing.rb +22 -13
- data/lib/grape/dsl/settings.rb +10 -6
- data/lib/grape/dsl/validations.rb +19 -14
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +67 -58
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +11 -7
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +4 -6
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/exceptions/base.rb +23 -16
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
- data/lib/grape/exceptions/invalid_formatter.rb +2 -0
- data/lib/grape/exceptions/invalid_message_body.rb +2 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
- data/lib/grape/exceptions/method_not_allowed.rb +2 -0
- data/lib/grape/exceptions/missing_group_type.rb +10 -1
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +10 -1
- data/lib/grape/exceptions/validation.rb +5 -8
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +16 -13
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
- data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
- data/lib/grape/extensions/hash.rb +2 -0
- data/lib/grape/extensions/hashie/mash.rb +2 -0
- data/lib/grape/formatter/json.rb +3 -0
- data/lib/grape/formatter/serializable_hash.rb +4 -1
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +3 -0
- data/lib/grape/formatter.rb +5 -3
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/locale/en.yml +11 -8
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +9 -2
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +13 -8
- data/lib/grape/middleware/error.rb +22 -17
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +12 -10
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +16 -6
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
- data/lib/grape/middleware/versioner/header.rb +13 -9
- data/lib/grape/middleware/versioner/param.rb +4 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
- data/lib/grape/middleware/versioner/path.rb +5 -1
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser/json.rb +3 -1
- data/lib/grape/parser/xml.rb +3 -1
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/path.rb +16 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +21 -9
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +21 -17
- data/lib/grape/router/route.rb +15 -29
- data/lib/grape/router.rb +36 -29
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/types/invalid_value.rb +8 -0
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +3 -3
- data/lib/grape/util/inheritable_values.rb +7 -25
- data/lib/grape/util/json.rb +4 -0
- data/lib/grape/util/lazy_block.rb +27 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +99 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +10 -35
- data/lib/grape/util/stackable_values.rb +21 -34
- data/lib/grape/util/strict_hash_configuration.rb +3 -1
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations/attributes_doc.rb +58 -0
- data/lib/grape/validations/attributes_iterator.rb +16 -6
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +174 -94
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types/array_coercer.rb +63 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +72 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +17 -0
- data/lib/grape/validations/types/json.rb +47 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +75 -0
- data/lib/grape/validations/types/set_coercer.rb +38 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/types.rb +106 -63
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +84 -68
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/default_validator.rb +51 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +27 -16
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +88 -0
- data/lib/grape/validations.rb +18 -6
- data/lib/grape/version.rb +3 -1
- data/lib/grape.rb +175 -94
- data/spec/grape/api/custom_validations_spec.rb +117 -44
- data/spec/grape/api/deeply_included_options_spec.rb +4 -4
- data/spec/grape/api/defines_boolean_in_params_spec.rb +38 -0
- data/spec/grape/api/documentation_spec.rb +59 -0
- data/spec/grape/api/inherited_helpers_spec.rb +1 -1
- data/spec/grape/api/instance_spec.rb +103 -0
- data/spec/grape/api/invalid_format_spec.rb +3 -1
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/nested_helpers_spec.rb +1 -1
- data/spec/grape/api/optional_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/parameters_modification_spec.rb +2 -2
- data/spec/grape/api/patch_method_helpers_spec.rb +1 -1
- data/spec/grape/api/recognize_path_spec.rb +2 -2
- data/spec/grape/api/required_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +10 -16
- data/spec/grape/api/shared_helpers_spec.rb +1 -1
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +995 -231
- data/spec/grape/config_spec.rb +17 -0
- data/spec/grape/dsl/callbacks_spec.rb +3 -2
- data/spec/grape/dsl/desc_spec.rb +43 -17
- data/spec/grape/dsl/headers_spec.rb +40 -10
- data/spec/grape/dsl/helpers_spec.rb +6 -5
- data/spec/grape/dsl/inside_route_spec.rb +189 -38
- data/spec/grape/dsl/logger_spec.rb +17 -19
- data/spec/grape/dsl/middleware_spec.rb +11 -2
- data/spec/grape/dsl/parameters_spec.rb +3 -1
- data/spec/grape/dsl/request_response_spec.rb +8 -7
- data/spec/grape/dsl/routing_spec.rb +22 -9
- data/spec/grape/dsl/settings_spec.rb +1 -1
- data/spec/grape/dsl/validations_spec.rb +1 -16
- data/spec/grape/endpoint/declared_spec.rb +846 -0
- data/spec/grape/endpoint_spec.rb +136 -577
- data/spec/grape/entity_spec.rb +31 -24
- data/spec/grape/exceptions/base_spec.rb +81 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +4 -1
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +65 -23
- data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -2
- data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +1 -1
- data/spec/grape/exceptions/missing_option_spec.rb +2 -2
- data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +21 -15
- data/spec/grape/exceptions/validation_spec.rb +6 -4
- data/spec/grape/extensions/param_builders/hash_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +9 -9
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +9 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -10
- data/spec/grape/integration/rack_spec.rb +25 -8
- data/spec/grape/loading_spec.rb +9 -9
- data/spec/grape/middleware/auth/base_spec.rb +2 -1
- data/spec/grape/middleware/auth/dsl_spec.rb +19 -10
- data/spec/grape/middleware/auth/strategies_spec.rb +62 -22
- data/spec/grape/middleware/base_spec.rb +36 -17
- data/spec/grape/middleware/error_spec.rb +11 -4
- data/spec/grape/middleware/exception_spec.rb +112 -162
- data/spec/grape/middleware/formatter_spec.rb +65 -29
- data/spec/grape/middleware/globals_spec.rb +8 -5
- data/spec/grape/middleware/stack_spec.rb +25 -13
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -2
- data/spec/grape/middleware/versioner/header_spec.rb +37 -14
- data/spec/grape/middleware/versioner/param_spec.rb +8 -2
- data/spec/grape/middleware/versioner/path_spec.rb +6 -2
- data/spec/grape/middleware/versioner_spec.rb +2 -2
- data/spec/grape/named_api_spec.rb +19 -0
- data/spec/grape/parser_spec.rb +10 -6
- data/spec/grape/path_spec.rb +53 -53
- data/spec/grape/presenters/presenter_spec.rb +8 -7
- data/spec/grape/request_spec.rb +29 -3
- data/spec/grape/util/inheritable_setting_spec.rb +9 -8
- data/spec/grape/util/inheritable_values_spec.rb +5 -3
- data/spec/grape/util/reverse_stackable_values_spec.rb +5 -2
- data/spec/grape/util/stackable_values_spec.rb +10 -7
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -1
- data/spec/grape/validations/attributes_doc_spec.rb +153 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +13 -14
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +40 -0
- data/spec/grape/validations/params_scope_spec.rb +568 -99
- data/spec/grape/validations/single_attribute_iterator_spec.rb +57 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +33 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +150 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +32 -0
- data/spec/grape/validations/types_spec.rb +44 -45
- data/spec/grape/validations/validators/all_or_none_spec.rb +134 -32
- data/spec/grape/validations/validators/allow_blank_spec.rb +137 -141
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +169 -31
- data/spec/grape/validations/validators/coerce_spec.rb +491 -151
- data/spec/grape/validations/validators/default_spec.rb +242 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +198 -40
- data/spec/grape/validations/validators/except_values_spec.rb +6 -5
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +181 -30
- data/spec/grape/validations/validators/presence_spec.rb +45 -2
- data/spec/grape/validations/validators/regexp_spec.rb +27 -33
- data/spec/grape/validations/validators/same_as_spec.rb +57 -0
- data/spec/grape/validations/validators/values_spec.rb +227 -180
- data/spec/grape/validations_spec.rb +502 -72
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -2
- data/spec/integration/multi_xml/xml_spec.rb +2 -2
- data/spec/shared/versioning_examples.rb +34 -29
- data/spec/spec_helper.rb +31 -5
- data/spec/support/basic_auth_encode_helpers.rb +3 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +8 -8
- metadata +111 -61
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
- data/lib/grape/util/content_types.rb +0 -26
- data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
- data/lib/grape/validations/validators/all_or_none.rb +0 -20
- data/lib/grape/validations/validators/allow_blank.rb +0 -16
- data/lib/grape/validations/validators/as.rb +0 -15
- data/lib/grape/validations/validators/at_least_one_of.rb +0 -20
- data/lib/grape/validations/validators/coerce.rb +0 -74
- data/lib/grape/validations/validators/default.rb +0 -48
- data/lib/grape/validations/validators/exactly_one_of.rb +0 -29
- data/lib/grape/validations/validators/except_values.rb +0 -20
- data/lib/grape/validations/validators/mutual_exclusion.rb +0 -25
- data/lib/grape/validations/validators/presence.rb +0 -10
- data/lib/grape/validations/validators/regexp.rb +0 -11
- data/lib/grape/validations/validators/values.rb +0 -71
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
- data/spec/grape/dsl/configuration_spec.rb +0 -14
- data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
data/README.md
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
![grape logo](grape.png)
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/grape.svg)](http://badge.fury.io/rb/grape)
|
4
|
-
[![Build Status](https://
|
5
|
-
[![Dependency Status](https://gemnasium.com/ruby-grape/grape.svg)](https://gemnasium.com/ruby-grape/grape)
|
4
|
+
[![Build Status](https://github.com/ruby-grape/grape/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape/actions)
|
6
5
|
[![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
|
7
6
|
[![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
|
8
|
-
[![Inline docs](
|
9
|
-
[![git.legal](https://git.legal/projects/1364/badge.svg "Number of libraries approved")](https://git.legal/projects/1364)
|
7
|
+
[![Inline docs](https://inch-ci.org/github/ruby-grape/grape.svg)](https://inch-ci.org/github/ruby-grape/grape)
|
10
8
|
[![Join the chat at https://gitter.im/ruby-grape/grape](https://badges.gitter.im/ruby-grape/grape.svg)](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,62 +12,77 @@
|
|
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)
|
34
41
|
- [Include Parent Namespaces](#include-parent-namespaces)
|
35
42
|
- [Include Missing](#include-missing)
|
43
|
+
- [Evaluate Given](#evaluate-given)
|
36
44
|
- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
|
37
45
|
- [Supported Parameter Types](#supported-parameter-types)
|
38
46
|
- [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
|
39
47
|
- [Custom Types and Coercions](#custom-types-and-coercions)
|
40
48
|
- [Multipart File Parameters](#multipart-file-parameters)
|
41
|
-
- [First-Class
|
49
|
+
- [First-Class JSON Types](#first-class-json-types)
|
42
50
|
- [Multiple Allowed Types](#multiple-allowed-types)
|
43
51
|
- [Validation of Nested Parameters](#validation-of-nested-parameters)
|
44
52
|
- [Dependent Parameters](#dependent-parameters)
|
45
53
|
- [Group Options](#group-options)
|
46
|
-
- [
|
54
|
+
- [Renaming](#renaming)
|
47
55
|
- [Built-in Validators](#built-in-validators)
|
48
|
-
- [
|
49
|
-
- [
|
50
|
-
- [
|
51
|
-
- [
|
52
|
-
- [
|
53
|
-
- [
|
54
|
-
- [
|
55
|
-
- [
|
56
|
-
- [
|
56
|
+
- [allow_blank](#allow_blank)
|
57
|
+
- [values](#values)
|
58
|
+
- [except_values](#except_values)
|
59
|
+
- [same_as](#same_as)
|
60
|
+
- [regexp](#regexp)
|
61
|
+
- [mutually_exclusive](#mutually_exclusive)
|
62
|
+
- [exactly_one_of](#exactly_one_of)
|
63
|
+
- [at_least_one_of](#at_least_one_of)
|
64
|
+
- [all_or_none_of](#all_or_none_of)
|
65
|
+
- [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
66
|
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
|
58
67
|
- [Custom Validators](#custom-validators)
|
59
68
|
- [Validation Errors](#validation-errors)
|
60
69
|
- [I18n](#i18n)
|
61
70
|
- [Custom Validation messages](#custom-validation-messages)
|
62
|
-
- [
|
63
|
-
- [
|
64
|
-
- [
|
65
|
-
- [
|
66
|
-
- [
|
67
|
-
- [
|
68
|
-
- [
|
69
|
-
- [
|
71
|
+
- [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
|
72
|
+
- [same_as](#same_as-1)
|
73
|
+
- [all_or_none_of](#all_or_none_of-1)
|
74
|
+
- [mutually_exclusive](#mutually_exclusive-1)
|
75
|
+
- [exactly_one_of](#exactly_one_of-1)
|
76
|
+
- [at_least_one_of](#at_least_one_of-1)
|
77
|
+
- [Coerce](#coerce)
|
78
|
+
- [With Lambdas](#with-lambdas)
|
79
|
+
- [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
|
70
80
|
- [Overriding Attribute Names](#overriding-attribute-names)
|
71
81
|
- [With Default](#with-default)
|
72
82
|
- [Headers](#headers)
|
83
|
+
- [Request](#request)
|
84
|
+
- [Header Case Handling](#header-case-handling)
|
85
|
+
- [Response](#response)
|
73
86
|
- [Routes](#routes)
|
74
87
|
- [Helpers](#helpers)
|
75
88
|
- [Path Helpers](#path-helpers)
|
@@ -105,7 +118,7 @@
|
|
105
118
|
- [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
|
106
119
|
- [Describing and Inspecting an API](#describing-and-inspecting-an-api)
|
107
120
|
- [Current Route and Endpoint](#current-route-and-endpoint)
|
108
|
-
- [Before and
|
121
|
+
- [Before, After and Finally](#before-after-and-finally)
|
109
122
|
- [Anchoring](#anchoring)
|
110
123
|
- [Using Custom Middleware](#using-custom-middleware)
|
111
124
|
- [Grape Middleware](#grape-middleware)
|
@@ -132,6 +145,7 @@
|
|
132
145
|
- [format_response.grape](#format_responsegrape)
|
133
146
|
- [Monitoring Products](#monitoring-products)
|
134
147
|
- [Contributing to Grape](#contributing-to-grape)
|
148
|
+
- [Security](#security)
|
135
149
|
- [License](#license)
|
136
150
|
- [Copyright](#copyright)
|
137
151
|
|
@@ -145,7 +159,7 @@ content negotiation, versioning and much more.
|
|
145
159
|
|
146
160
|
## Stable Release
|
147
161
|
|
148
|
-
You're reading the documentation for the stable release of Grape,
|
162
|
+
You're reading the documentation for the stable release of Grape, 1.7.1.
|
149
163
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
150
164
|
|
151
165
|
## Project Resources
|
@@ -155,17 +169,19 @@ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
|
155
169
|
* Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
|
156
170
|
* [Follow us on Twitter](https://twitter.com/grapeframework)
|
157
171
|
|
158
|
-
##
|
172
|
+
## Grape for Enterprise
|
173
|
+
|
174
|
+
Available as part of the Tidelift Subscription.
|
159
175
|
|
160
|
-
Grape
|
176
|
+
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.
|
161
177
|
|
162
|
-
|
178
|
+
## Installation
|
163
179
|
|
164
|
-
|
180
|
+
Ruby 2.4 or newer is required.
|
165
181
|
|
166
|
-
|
182
|
+
Grape is available as a gem, to install it run:
|
167
183
|
|
168
|
-
|
184
|
+
bundle add grape
|
169
185
|
|
170
186
|
## Basic Usage
|
171
187
|
|
@@ -204,7 +220,7 @@ module Twitter
|
|
204
220
|
|
205
221
|
desc 'Return a status.'
|
206
222
|
params do
|
207
|
-
requires :id, type: Integer, desc: 'Status
|
223
|
+
requires :id, type: Integer, desc: 'Status ID.'
|
208
224
|
end
|
209
225
|
route_param :id do
|
210
226
|
get do
|
@@ -252,6 +268,17 @@ end
|
|
252
268
|
|
253
269
|
## Mounting
|
254
270
|
|
271
|
+
### All
|
272
|
+
|
273
|
+
|
274
|
+
By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
Twitter::API.compile!
|
278
|
+
```
|
279
|
+
|
280
|
+
This can be added to your `config.ru` (if using rackup), `application.rb` (if using rails), or any file that loads your server.
|
281
|
+
|
255
282
|
### Rack
|
256
283
|
|
257
284
|
The above sample creates a Rack application that can be run from a rackup `config.ru` file
|
@@ -261,6 +288,13 @@ with `rackup`:
|
|
261
288
|
run Twitter::API
|
262
289
|
```
|
263
290
|
|
291
|
+
(With pre-loading you can use)
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
Twitter::API.compile!
|
295
|
+
run Twitter::API
|
296
|
+
```
|
297
|
+
|
264
298
|
And would respond to the following routes:
|
265
299
|
|
266
300
|
GET /api/statuses/public_timeline
|
@@ -277,13 +311,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
|
|
277
311
|
If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
|
278
312
|
is handled correctly.
|
279
313
|
|
314
|
+
#### Rails 4
|
315
|
+
|
280
316
|
The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
|
281
317
|
`config.ru` before mounting Grape, e.g.:
|
282
318
|
|
283
319
|
```ruby
|
284
320
|
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
321
|
+
```
|
285
322
|
|
286
|
-
|
323
|
+
#### Rails 5+
|
324
|
+
|
325
|
+
Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
|
326
|
+
|
327
|
+
```ruby
|
328
|
+
use OTR::ActiveRecord::ConnectionManagement
|
287
329
|
```
|
288
330
|
|
289
331
|
### Alongside Sinatra (or other frameworks)
|
@@ -310,13 +352,24 @@ class Web < Sinatra::Base
|
|
310
352
|
end
|
311
353
|
|
312
354
|
use Rack::Session::Cookie
|
313
|
-
run Rack::Cascade.new [
|
355
|
+
run Rack::Cascade.new [Web, API]
|
314
356
|
```
|
315
357
|
|
358
|
+
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).
|
359
|
+
|
360
|
+
|
316
361
|
### Rails
|
317
362
|
|
318
363
|
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
364
|
|
365
|
+
Modify `config/routes`:
|
366
|
+
|
367
|
+
```ruby
|
368
|
+
mount Twitter::API => '/'
|
369
|
+
```
|
370
|
+
|
371
|
+
#### Rails < 5.2
|
372
|
+
|
320
373
|
Modify `application.rb`:
|
321
374
|
|
322
375
|
```ruby
|
@@ -324,14 +377,18 @@ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
|
|
324
377
|
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
325
378
|
```
|
326
379
|
|
327
|
-
|
380
|
+
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
381
|
+
|
382
|
+
#### Rails 6.0
|
383
|
+
|
384
|
+
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
385
|
|
329
386
|
```ruby
|
330
|
-
|
387
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
388
|
+
inflect.acronym 'API'
|
389
|
+
end
|
331
390
|
```
|
332
391
|
|
333
|
-
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
334
|
-
|
335
392
|
### Modules
|
336
393
|
|
337
394
|
You can mount multiple API implementations inside another one. These don't have to be
|
@@ -365,6 +422,131 @@ class Twitter::API < Grape::API
|
|
365
422
|
end
|
366
423
|
```
|
367
424
|
|
425
|
+
## Remounting
|
426
|
+
|
427
|
+
You can mount the same endpoints in two different locations.
|
428
|
+
|
429
|
+
```ruby
|
430
|
+
class Voting::API < Grape::API
|
431
|
+
namespace 'votes' do
|
432
|
+
get do
|
433
|
+
# Your logic
|
434
|
+
end
|
435
|
+
|
436
|
+
post do
|
437
|
+
# Your logic
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
class Post::API < Grape::API
|
443
|
+
mount Voting::API
|
444
|
+
end
|
445
|
+
|
446
|
+
class Comment::API < Grape::API
|
447
|
+
mount Voting::API
|
448
|
+
end
|
449
|
+
```
|
450
|
+
|
451
|
+
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`.
|
452
|
+
|
453
|
+
### Mount Configuration
|
454
|
+
|
455
|
+
You can configure remountable endpoints to change how they behave according to where they are mounted.
|
456
|
+
|
457
|
+
```ruby
|
458
|
+
class Voting::API < Grape::API
|
459
|
+
namespace 'votes' do
|
460
|
+
desc "Vote for your #{configuration[:votable]}"
|
461
|
+
get do
|
462
|
+
# Your logic
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
class Post::API < Grape::API
|
468
|
+
mount Voting::API, with: { votable: 'posts' }
|
469
|
+
end
|
470
|
+
|
471
|
+
class Comment::API < Grape::API
|
472
|
+
mount Voting::API, with: { votable: 'comments' }
|
473
|
+
end
|
474
|
+
```
|
475
|
+
|
476
|
+
Note that if you're passing a hash as the first parameter to `mount`, you will need to explicitly put `()` around parameters:
|
477
|
+
```ruby
|
478
|
+
# good
|
479
|
+
mount({ ::Some::Api => '/some/api' }, with: { condition: true })
|
480
|
+
|
481
|
+
# bad
|
482
|
+
mount ::Some::Api => '/some/api', with: { condition: true }
|
483
|
+
```
|
484
|
+
|
485
|
+
You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
|
486
|
+
|
487
|
+
If you want logic happening given on an `configuration`, you can use the helper `given`.
|
488
|
+
|
489
|
+
```ruby
|
490
|
+
class ConditionalEndpoint::API < Grape::API
|
491
|
+
given configuration[:some_setting] do
|
492
|
+
get 'mount_this_endpoint_conditionally' do
|
493
|
+
configuration[:configurable_response]
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
```
|
498
|
+
|
499
|
+
If you want a block of logic running every time an endpoint is mounted (within which you can access the `configuration` Hash)
|
500
|
+
|
501
|
+
|
502
|
+
```ruby
|
503
|
+
class ConditionalEndpoint::API < Grape::API
|
504
|
+
mounted do
|
505
|
+
YourLogger.info "This API was mounted at: #{Time.now}"
|
506
|
+
|
507
|
+
get configuration[:endpoint_name] do
|
508
|
+
configuration[:configurable_response]
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
```
|
513
|
+
|
514
|
+
More complex results can be achieved by using `mounted` as an expression within which the `configuration` is already evaluated as a Hash.
|
515
|
+
|
516
|
+
```ruby
|
517
|
+
class ExpressionEndpointAPI < Grape::API
|
518
|
+
get(mounted { configuration[:route_name] || 'default_name' }) do
|
519
|
+
# some logic
|
520
|
+
end
|
521
|
+
end
|
522
|
+
```
|
523
|
+
|
524
|
+
```ruby
|
525
|
+
class BasicAPI < Grape::API
|
526
|
+
desc 'Statuses index' do
|
527
|
+
params: mounted { configuration[:entity] || API::Entities::Status }.documentation
|
528
|
+
end
|
529
|
+
params do
|
530
|
+
requires :all, using: mounted { configuration[:entity] || API::Entities::Status }.documentation
|
531
|
+
end
|
532
|
+
get '/statuses' do
|
533
|
+
statuses = Status.all
|
534
|
+
type = current_user.admin? ? :full : :default
|
535
|
+
present statuses, with: mounted { configuration[:entity] || API::Entities::Status }, type: type
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
class V1 < Grape::API
|
540
|
+
version 'v1'
|
541
|
+
mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Entities::Status } }
|
542
|
+
end
|
543
|
+
|
544
|
+
class V2 < Grape::API
|
545
|
+
version 'v2'
|
546
|
+
mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Entities::V2::Status } }
|
547
|
+
end
|
548
|
+
```
|
549
|
+
|
368
550
|
## Versioning
|
369
551
|
|
370
552
|
There are four strategies in which clients can reach your API's endpoints: `:path`,
|
@@ -445,14 +627,18 @@ version 'v1', using: :param, parameter: 'v'
|
|
445
627
|
|
446
628
|
## Describing Methods
|
447
629
|
|
448
|
-
You can add a description to API methods and namespaces.
|
630
|
+
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.
|
631
|
+
|
632
|
+
Note: Description block is only for documentation and won't affects API behavior.
|
449
633
|
|
450
634
|
```ruby
|
451
635
|
desc 'Returns your public timeline.' do
|
636
|
+
summary 'summary'
|
452
637
|
detail 'more details'
|
453
638
|
params API::Entities::Status.documentation
|
454
639
|
success API::Entities::Entity
|
455
640
|
failure [[401, 'Unauthorized', 'Entities::Error']]
|
641
|
+
default { code: 500, message: 'InvalidRequest', model: Entities::Error }
|
456
642
|
named 'My named route'
|
457
643
|
headers XAuthToken: {
|
458
644
|
description: 'Validates your identity',
|
@@ -462,7 +648,13 @@ desc 'Returns your public timeline.' do
|
|
462
648
|
description: 'Not really needed',
|
463
649
|
required: false
|
464
650
|
}
|
465
|
-
|
651
|
+
hidden false
|
652
|
+
deprecated false
|
653
|
+
is_array true
|
654
|
+
nickname 'nickname'
|
655
|
+
produces ['application/json']
|
656
|
+
consumes ['application/json']
|
657
|
+
tags ['tag1', 'tag2']
|
466
658
|
end
|
467
659
|
get :public_timeline do
|
468
660
|
Status.limit(20)
|
@@ -471,10 +663,48 @@ end
|
|
471
663
|
|
472
664
|
* `detail`: A more enhanced description
|
473
665
|
* `params`: Define parameters directly from an `Entity`
|
474
|
-
* `success`: (former entity) The `Entity` to be used to present
|
475
|
-
* `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities
|
666
|
+
* `success`: (former entity) The `Entity` to be used to present the success response for this route.
|
667
|
+
* `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities.
|
668
|
+
* `default`: The definition and `Entity` used to present the default response for this route.
|
476
669
|
* `named`: A helper to give a route a name and find it with this name in the documentation Hash
|
477
670
|
* `headers`: A definition of the used Headers
|
671
|
+
* Other options can be found in [grape-swagger][grape-swagger]
|
672
|
+
|
673
|
+
[grape-swagger]: https://github.com/ruby-grape/grape-swagger
|
674
|
+
|
675
|
+
## Configuration
|
676
|
+
|
677
|
+
Use `Grape.configure` to set up global settings at load time.
|
678
|
+
Currently the configurable settings are:
|
679
|
+
|
680
|
+
* `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
|
681
|
+
|
682
|
+
To change a setting value make sure that at some point during load time the following code runs
|
683
|
+
|
684
|
+
```ruby
|
685
|
+
Grape.configure do |config|
|
686
|
+
config.setting = value
|
687
|
+
end
|
688
|
+
```
|
689
|
+
|
690
|
+
For example, for the `param_builder`, the following code could run in an initializer:
|
691
|
+
|
692
|
+
```ruby
|
693
|
+
Grape.configure do |config|
|
694
|
+
config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
|
695
|
+
end
|
696
|
+
```
|
697
|
+
|
698
|
+
You can also configure a single API:
|
699
|
+
|
700
|
+
```ruby
|
701
|
+
API.configure do |config|
|
702
|
+
config[key] = value
|
703
|
+
end
|
704
|
+
```
|
705
|
+
|
706
|
+
This will be available inside the API with `configuration`, as if it were
|
707
|
+
[mount configuration](#mount-configuration).
|
478
708
|
|
479
709
|
## Parameters
|
480
710
|
|
@@ -553,13 +783,21 @@ params do
|
|
553
783
|
end
|
554
784
|
```
|
555
785
|
|
786
|
+
Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
|
787
|
+
|
556
788
|
In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
|
557
789
|
|
558
790
|
Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
|
559
791
|
|
560
792
|
### Declared
|
561
793
|
|
562
|
-
Grape allows you to access only the parameters that have been declared by your `params` block. It
|
794
|
+
Grape allows you to access only the parameters that have been declared by your `params` block. It will:
|
795
|
+
|
796
|
+
* Filter out the params that have been passed, but are not allowed.
|
797
|
+
* Include any optional params that are declared but not passed.
|
798
|
+
* Perform any parameter renaming on the resulting hash.
|
799
|
+
|
800
|
+
Consider the following API endpoint:
|
563
801
|
|
564
802
|
````ruby
|
565
803
|
format :json
|
@@ -592,9 +830,9 @@ Once we add parameters requirements, grape will start returning only the declare
|
|
592
830
|
format :json
|
593
831
|
|
594
832
|
params do
|
595
|
-
|
596
|
-
|
597
|
-
|
833
|
+
optional :user, type: Hash do
|
834
|
+
optional :first_name, type: String
|
835
|
+
optional :last_name, type: String
|
598
836
|
end
|
599
837
|
end
|
600
838
|
|
@@ -622,6 +860,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
622
860
|
}
|
623
861
|
````
|
624
862
|
|
863
|
+
Missing params that are declared as type `Hash` or `Array` will be included.
|
864
|
+
|
865
|
+
````ruby
|
866
|
+
format :json
|
867
|
+
|
868
|
+
params do
|
869
|
+
optional :user, type: Hash do
|
870
|
+
optional :first_name, type: String
|
871
|
+
optional :last_name, type: String
|
872
|
+
end
|
873
|
+
optional :widgets, type: Array
|
874
|
+
end
|
875
|
+
|
876
|
+
post 'users/signup' do
|
877
|
+
{ 'declared_params' => declared(params) }
|
878
|
+
end
|
879
|
+
````
|
880
|
+
|
881
|
+
**Request**
|
882
|
+
|
883
|
+
````bash
|
884
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
|
885
|
+
````
|
886
|
+
|
887
|
+
**Response**
|
888
|
+
|
889
|
+
````json
|
890
|
+
{
|
891
|
+
"declared_params": {
|
892
|
+
"user": {
|
893
|
+
"first_name": null,
|
894
|
+
"last_name": null
|
895
|
+
},
|
896
|
+
"widgets": []
|
897
|
+
}
|
898
|
+
}
|
899
|
+
````
|
900
|
+
|
625
901
|
The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
|
626
902
|
|
627
903
|
The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
|
@@ -680,8 +956,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
|
|
680
956
|
format :json
|
681
957
|
|
682
958
|
params do
|
683
|
-
requires :
|
684
|
-
|
959
|
+
requires :user, type: Hash do
|
960
|
+
requires :first_name, type: String
|
961
|
+
optional :last_name, type: String
|
962
|
+
end
|
685
963
|
end
|
686
964
|
|
687
965
|
post 'users/signup' do
|
@@ -712,8 +990,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
712
990
|
````json
|
713
991
|
{
|
714
992
|
"declared_params": {
|
715
|
-
"
|
716
|
-
|
993
|
+
"user": {
|
994
|
+
"first_name": "first name",
|
995
|
+
"last_name": null
|
996
|
+
}
|
717
997
|
}
|
718
998
|
}
|
719
999
|
````
|
@@ -800,6 +1080,102 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
800
1080
|
}
|
801
1081
|
````
|
802
1082
|
|
1083
|
+
### Evaluate Given
|
1084
|
+
|
1085
|
+
By default `declared(params)` will not evaluate `given` and return all parameters. Use `evaluate_given` to evaluate all `given` blocks and return only parameters that satisfy `given` conditions. Consider the following API:
|
1086
|
+
|
1087
|
+
````ruby
|
1088
|
+
format :json
|
1089
|
+
|
1090
|
+
params do
|
1091
|
+
optional :child_id, type: Integer
|
1092
|
+
given :child_id do
|
1093
|
+
requires :father_id, type: Integer
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
post 'child' do
|
1098
|
+
{ 'declared_params' => declared(params, evaluate_given: true) }
|
1099
|
+
end
|
1100
|
+
````
|
1101
|
+
|
1102
|
+
**Request**
|
1103
|
+
|
1104
|
+
````bash
|
1105
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"father_id": 1}'
|
1106
|
+
````
|
1107
|
+
|
1108
|
+
**Response with evaluate_given:false**
|
1109
|
+
|
1110
|
+
````json
|
1111
|
+
{
|
1112
|
+
"declared_params": {
|
1113
|
+
"child_id": null,
|
1114
|
+
"father_id": 1
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
````
|
1118
|
+
|
1119
|
+
**Response with evaluate_given:true**
|
1120
|
+
|
1121
|
+
````json
|
1122
|
+
{
|
1123
|
+
"declared_params": {
|
1124
|
+
"child_id": null
|
1125
|
+
}
|
1126
|
+
}
|
1127
|
+
````
|
1128
|
+
|
1129
|
+
It also works on nested hashes:
|
1130
|
+
|
1131
|
+
````ruby
|
1132
|
+
format :json
|
1133
|
+
|
1134
|
+
params do
|
1135
|
+
requires :child, type: Hash do
|
1136
|
+
optional :child_id, type: Integer
|
1137
|
+
given :child_id do
|
1138
|
+
requires :father_id, type: Integer
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
post 'child' do
|
1144
|
+
{ 'declared_params' => declared(params, evaluate_given: true) }
|
1145
|
+
end
|
1146
|
+
````
|
1147
|
+
|
1148
|
+
**Request**
|
1149
|
+
|
1150
|
+
````bash
|
1151
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"child": {"father_id": 1}}'
|
1152
|
+
````
|
1153
|
+
|
1154
|
+
**Response with evaluate_given:false**
|
1155
|
+
|
1156
|
+
````json
|
1157
|
+
{
|
1158
|
+
"declared_params": {
|
1159
|
+
"child": {
|
1160
|
+
"child_id": null,
|
1161
|
+
"father_id": 1
|
1162
|
+
}
|
1163
|
+
}
|
1164
|
+
}
|
1165
|
+
````
|
1166
|
+
|
1167
|
+
**Response with evaluate_given:true**
|
1168
|
+
|
1169
|
+
````json
|
1170
|
+
{
|
1171
|
+
"declared_params": {
|
1172
|
+
"child": {
|
1173
|
+
"child_id": null
|
1174
|
+
}
|
1175
|
+
}
|
1176
|
+
}
|
1177
|
+
````
|
1178
|
+
|
803
1179
|
## Parameter Validation and Coercion
|
804
1180
|
|
805
1181
|
You can define validations and coercion options for your parameters using a `params` block.
|
@@ -834,13 +1210,13 @@ params do
|
|
834
1210
|
end
|
835
1211
|
```
|
836
1212
|
|
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
1213
|
Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
|
841
1214
|
number for each call to the endpoint of this `params` block. To have the default evaluate
|
842
1215
|
lazily with each request use a lambda, like `:random_number` above.
|
843
1216
|
|
1217
|
+
Note that default values will be passed through to any validation options specified.
|
1218
|
+
The following example will always fail if `:color` is not explicitly provided.
|
1219
|
+
|
844
1220
|
```ruby
|
845
1221
|
params do
|
846
1222
|
optional :color, type: String, default: 'blue', values: ['red', 'green']
|
@@ -900,7 +1276,8 @@ Aside from the default set of supported types listed above, any class can be
|
|
900
1276
|
used as a type as long as an explicit coercion method is supplied. If the type
|
901
1277
|
implements a class-level `parse` method, Grape will use it automatically.
|
902
1278
|
This method must take one string argument and return an instance of the correct
|
903
|
-
type, or
|
1279
|
+
type, or return an instance of `Grape::Types::InvalidValue` which optionally
|
1280
|
+
accepts a message to be returned in the response.
|
904
1281
|
|
905
1282
|
```ruby
|
906
1283
|
class Color
|
@@ -910,8 +1287,9 @@ class Color
|
|
910
1287
|
end
|
911
1288
|
|
912
1289
|
def self.parse(value)
|
913
|
-
|
914
|
-
|
1290
|
+
return new(value) if %w[blue red green]).include?(value)
|
1291
|
+
|
1292
|
+
Grape::Types::InvalidValue.new('Unsupported color')
|
915
1293
|
end
|
916
1294
|
end
|
917
1295
|
|
@@ -934,7 +1312,7 @@ parameter, and the return value must match the given `type`.
|
|
934
1312
|
|
935
1313
|
```ruby
|
936
1314
|
params do
|
937
|
-
requires :passwd, type: String, coerce_with: Base64.method(:
|
1315
|
+
requires :passwd, type: String, coerce_with: Base64.method(:decode64)
|
938
1316
|
requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
|
939
1317
|
|
940
1318
|
requires :obj, type: Hash, coerce_with: JSON do
|
@@ -943,6 +1321,7 @@ params do
|
|
943
1321
|
end
|
944
1322
|
end
|
945
1323
|
```
|
1324
|
+
Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
|
946
1325
|
|
947
1326
|
Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
|
948
1327
|
It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
|
@@ -1109,6 +1488,18 @@ params do
|
|
1109
1488
|
end
|
1110
1489
|
```
|
1111
1490
|
|
1491
|
+
You can rename parameters:
|
1492
|
+
|
1493
|
+
```ruby
|
1494
|
+
params do
|
1495
|
+
optional :category, as: :type
|
1496
|
+
given type: ->(val) { val == 'foo' } do
|
1497
|
+
requires :description
|
1498
|
+
end
|
1499
|
+
end
|
1500
|
+
```
|
1501
|
+
|
1502
|
+
Note: param in `given` should be the renamed one. In the example, it should be `type`, not `category`.
|
1112
1503
|
|
1113
1504
|
### Group Options
|
1114
1505
|
|
@@ -1137,9 +1528,9 @@ params do
|
|
1137
1528
|
end
|
1138
1529
|
```
|
1139
1530
|
|
1140
|
-
###
|
1531
|
+
### Renaming
|
1141
1532
|
|
1142
|
-
You can
|
1533
|
+
You can rename parameters using `as`, which can be useful when refactoring existing APIs:
|
1143
1534
|
|
1144
1535
|
```ruby
|
1145
1536
|
resource :users do
|
@@ -1153,7 +1544,7 @@ resource :users do
|
|
1153
1544
|
end
|
1154
1545
|
```
|
1155
1546
|
|
1156
|
-
The value passed to `as` will be the key when calling `
|
1547
|
+
The value passed to `as` will be the key when calling `declared(params)`.
|
1157
1548
|
|
1158
1549
|
### Built-in Validators
|
1159
1550
|
|
@@ -1197,6 +1588,15 @@ params do
|
|
1197
1588
|
end
|
1198
1589
|
```
|
1199
1590
|
|
1591
|
+
Note endless ranges are also supported with ActiveSupport >= 6.0, but they require that the type be provided.
|
1592
|
+
|
1593
|
+
```ruby
|
1594
|
+
params do
|
1595
|
+
requires :minimum, type: Integer, values: 10..
|
1596
|
+
optional :maximum, type: Integer, values: ..10
|
1597
|
+
end
|
1598
|
+
```
|
1599
|
+
|
1200
1600
|
Note that *both* range endpoints have to be a `#kind_of?` your `:type` option (if you don't supply the `:type` option, it will be guessed to be equal to the class of the range's first endpoint). So the following is invalid:
|
1201
1601
|
|
1202
1602
|
```ruby
|
@@ -1232,6 +1632,14 @@ end
|
|
1232
1632
|
|
1233
1633
|
While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
|
1234
1634
|
|
1635
|
+
Note that [allow_blank](#allow_blank) validator applies while using `:values`. In the following example the absence of `:allow_blank` does not prevent `:state` from receiving blank values because `:allow_blank` defaults to `true`.
|
1636
|
+
|
1637
|
+
```ruby
|
1638
|
+
params do
|
1639
|
+
requires :state, type: Symbol, values: [:active, :inactive]
|
1640
|
+
end
|
1641
|
+
```
|
1642
|
+
|
1235
1643
|
#### `except_values`
|
1236
1644
|
|
1237
1645
|
Parameters can be restricted from having a specific set of values with the `:except_values` option.
|
@@ -1248,6 +1656,17 @@ params do
|
|
1248
1656
|
end
|
1249
1657
|
```
|
1250
1658
|
|
1659
|
+
#### `same_as`
|
1660
|
+
|
1661
|
+
A `same_as` option can be given to ensure that values of parameters match.
|
1662
|
+
|
1663
|
+
```ruby
|
1664
|
+
params do
|
1665
|
+
requires :password
|
1666
|
+
requires :password_confirmation, same_as: :password
|
1667
|
+
end
|
1668
|
+
```
|
1669
|
+
|
1251
1670
|
#### `regexp`
|
1252
1671
|
|
1253
1672
|
Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
|
@@ -1423,10 +1842,10 @@ end
|
|
1423
1842
|
### Custom Validators
|
1424
1843
|
|
1425
1844
|
```ruby
|
1426
|
-
class AlphaNumeric < Grape::Validations::Base
|
1845
|
+
class AlphaNumeric < Grape::Validations::Validators::Base
|
1427
1846
|
def validate_param!(attr_name, params)
|
1428
1847
|
unless params[attr_name] =~ /\A[[:alnum:]]+\z/
|
1429
|
-
|
1848
|
+
raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: 'must consist of alpha-numeric characters'
|
1430
1849
|
end
|
1431
1850
|
end
|
1432
1851
|
end
|
@@ -1441,10 +1860,10 @@ end
|
|
1441
1860
|
You can also create custom classes that take parameters.
|
1442
1861
|
|
1443
1862
|
```ruby
|
1444
|
-
class Length < Grape::Validations::Base
|
1863
|
+
class Length < Grape::Validations::Validators::Base
|
1445
1864
|
def validate_param!(attr_name, params)
|
1446
1865
|
unless params[attr_name].length <= @option
|
1447
|
-
|
1866
|
+
raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
|
1448
1867
|
end
|
1449
1868
|
end
|
1450
1869
|
end
|
@@ -1459,7 +1878,7 @@ end
|
|
1459
1878
|
You can also create custom validation that use request to validate the attribute. For example if you want to have parameters that are available to only admins, you can do the following.
|
1460
1879
|
|
1461
1880
|
```ruby
|
1462
|
-
class Admin < Grape::Validations::Base
|
1881
|
+
class Admin < Grape::Validations::Validators::Base
|
1463
1882
|
def validate(request)
|
1464
1883
|
# return if the param we are checking was not in request
|
1465
1884
|
# @attrs is a list containing the attribute we are currently validating
|
@@ -1470,7 +1889,7 @@ class Admin < Grape::Validations::Base
|
|
1470
1889
|
return unless @option
|
1471
1890
|
# check if user is admin or not
|
1472
1891
|
# as an example get a token from request and check if it's admin or not
|
1473
|
-
|
1892
|
+
raise Grape::Exceptions::Validation.new params: @attrs, message: 'Can not set admin-only field.' unless request.headers['X-Access-Token'] == 'admin'
|
1474
1893
|
end
|
1475
1894
|
end
|
1476
1895
|
```
|
@@ -1485,7 +1904,7 @@ params do
|
|
1485
1904
|
end
|
1486
1905
|
```
|
1487
1906
|
|
1488
|
-
Every validation will have
|
1907
|
+
Every validation will have its own instance of the validator, which means that the validator can have a state.
|
1489
1908
|
|
1490
1909
|
### Validation Errors
|
1491
1910
|
|
@@ -1546,6 +1965,8 @@ end
|
|
1546
1965
|
Grape supports I18n for parameter-related error messages, but will fallback to English if
|
1547
1966
|
translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
|
1548
1967
|
|
1968
|
+
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.
|
1969
|
+
|
1549
1970
|
### Custom Validation messages
|
1550
1971
|
|
1551
1972
|
Grape supports custom validation messages for parameter-related and coerce-related error messages.
|
@@ -1558,6 +1979,15 @@ params do
|
|
1558
1979
|
end
|
1559
1980
|
```
|
1560
1981
|
|
1982
|
+
#### `same_as`
|
1983
|
+
|
1984
|
+
```ruby
|
1985
|
+
params do
|
1986
|
+
requires :password
|
1987
|
+
requires :password_confirmation, same_as: { value: :password, message: 'not match' }
|
1988
|
+
end
|
1989
|
+
```
|
1990
|
+
|
1561
1991
|
#### `all_or_none_of`
|
1562
1992
|
|
1563
1993
|
```ruby
|
@@ -1587,7 +2017,7 @@ params do
|
|
1587
2017
|
optional :beer
|
1588
2018
|
optional :wine
|
1589
2019
|
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"}
|
2020
|
+
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
2021
|
end
|
1592
2022
|
```
|
1593
2023
|
|
@@ -1606,7 +2036,7 @@ end
|
|
1606
2036
|
|
1607
2037
|
```ruby
|
1608
2038
|
params do
|
1609
|
-
requires :int, type: {value: Integer, message: "type cast is invalid" }
|
2039
|
+
requires :int, type: { value: Integer, message: "type cast is invalid" }
|
1610
2040
|
end
|
1611
2041
|
```
|
1612
2042
|
|
@@ -1668,6 +2098,7 @@ end
|
|
1668
2098
|
|
1669
2099
|
## Headers
|
1670
2100
|
|
2101
|
+
### Request
|
1671
2102
|
Request headers are available through the `headers` helper or from `env` in their original form.
|
1672
2103
|
|
1673
2104
|
```ruby
|
@@ -1682,13 +2113,30 @@ get do
|
|
1682
2113
|
end
|
1683
2114
|
```
|
1684
2115
|
|
2116
|
+
#### Header Case Handling
|
2117
|
+
|
2118
|
+
The above example may have been requested as follows:
|
2119
|
+
|
2120
|
+
``` shell
|
2121
|
+
curl -H "secret_PassWord: swordfish" ...
|
2122
|
+
```
|
2123
|
+
|
2124
|
+
The header name will have been normalized for you.
|
2125
|
+
|
2126
|
+
- In the `header` helper names will be coerced into a capitalized kebab case.
|
2127
|
+
- In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_'.
|
2128
|
+
|
2129
|
+
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.
|
2130
|
+
|
2131
|
+
### Response
|
2132
|
+
|
1685
2133
|
You can set a response header with `header` inside an API.
|
1686
2134
|
|
1687
2135
|
```ruby
|
1688
2136
|
header 'X-Robots-Tag', 'noindex'
|
1689
2137
|
```
|
1690
2138
|
|
1691
|
-
When raising `error!`, pass additional headers as arguments.
|
2139
|
+
When raising `error!`, pass additional headers as arguments. Additional headers will be merged with headers set before `error!` call.
|
1692
2140
|
|
1693
2141
|
```ruby
|
1694
2142
|
error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
|
@@ -1696,6 +2144,56 @@ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
|
|
1696
2144
|
|
1697
2145
|
## Routes
|
1698
2146
|
|
2147
|
+
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`.
|
2148
|
+
Parts of the path that are denoted with a colon will be interpreted as route parameters.
|
2149
|
+
|
2150
|
+
```ruby
|
2151
|
+
route :get, 'status' do
|
2152
|
+
end
|
2153
|
+
|
2154
|
+
# is the same as
|
2155
|
+
|
2156
|
+
get 'status' do
|
2157
|
+
end
|
2158
|
+
|
2159
|
+
# is the same as
|
2160
|
+
|
2161
|
+
get :status do
|
2162
|
+
end
|
2163
|
+
|
2164
|
+
# is NOT the same as
|
2165
|
+
|
2166
|
+
get ':status' do # this makes params[:status] available
|
2167
|
+
end
|
2168
|
+
|
2169
|
+
# This will make both params[:status_id] and params[:id] available
|
2170
|
+
|
2171
|
+
get 'statuses/:status_id/reviews/:id' do
|
2172
|
+
end
|
2173
|
+
```
|
2174
|
+
|
2175
|
+
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.
|
2176
|
+
|
2177
|
+
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.
|
2178
|
+
|
2179
|
+
```ruby
|
2180
|
+
route_param :id, type: Integer do
|
2181
|
+
get 'status' do
|
2182
|
+
end
|
2183
|
+
end
|
2184
|
+
|
2185
|
+
# is the same as
|
2186
|
+
|
2187
|
+
namespace ':id' do
|
2188
|
+
params do
|
2189
|
+
requires :id, type: Integer
|
2190
|
+
end
|
2191
|
+
|
2192
|
+
get 'status' do
|
2193
|
+
end
|
2194
|
+
end
|
2195
|
+
```
|
2196
|
+
|
1699
2197
|
Optionally, you can define requirements for your named route parameters using regular
|
1700
2198
|
expressions on namespace or endpoint. The route will match only if all requirements are met.
|
1701
2199
|
|
@@ -1850,6 +2348,18 @@ params do
|
|
1850
2348
|
end
|
1851
2349
|
```
|
1852
2350
|
|
2351
|
+
If documentation isn't needed (for instance, it is an internal API), documentation can be disabled.
|
2352
|
+
|
2353
|
+
```ruby
|
2354
|
+
class API < Grape::API
|
2355
|
+
do_not_document!
|
2356
|
+
|
2357
|
+
# endpoints...
|
2358
|
+
end
|
2359
|
+
```
|
2360
|
+
|
2361
|
+
In this case, Grape won't create objects related to documentation which are retained in RAM forever.
|
2362
|
+
|
1853
2363
|
## Cookies
|
1854
2364
|
|
1855
2365
|
You can set, get and delete your cookies very simply using `cookies` method.
|
@@ -2028,6 +2538,12 @@ instead of a message.
|
|
2028
2538
|
error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
|
2029
2539
|
```
|
2030
2540
|
|
2541
|
+
You can set additional headers for the response. They will be merged with headers set before `error!` call.
|
2542
|
+
|
2543
|
+
```ruby
|
2544
|
+
error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
|
2545
|
+
```
|
2546
|
+
|
2031
2547
|
You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
|
2032
2548
|
|
2033
2549
|
```ruby
|
@@ -2182,7 +2698,7 @@ You can also rescue all exceptions with a code block and handle the Rack respons
|
|
2182
2698
|
```ruby
|
2183
2699
|
class Twitter::API < Grape::API
|
2184
2700
|
rescue_from :all do |e|
|
2185
|
-
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
|
2701
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
|
2186
2702
|
end
|
2187
2703
|
end
|
2188
2704
|
```
|
@@ -2253,9 +2769,9 @@ class Twitter::API < Grape::API
|
|
2253
2769
|
end
|
2254
2770
|
```
|
2255
2771
|
|
2256
|
-
The `rescue_from`
|
2772
|
+
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
2773
|
|
2258
|
-
|
2774
|
+
Alternately, use the `with` option in `rescue_from` to specify a method or a `proc`.
|
2259
2775
|
|
2260
2776
|
```ruby
|
2261
2777
|
class Twitter::API < Grape::API
|
@@ -2271,6 +2787,17 @@ class Twitter::API < Grape::API
|
|
2271
2787
|
end
|
2272
2788
|
```
|
2273
2789
|
|
2790
|
+
Inside the `rescue_from` block, the environment of the original controller method(`.self` receiver) is accessible through the `#context` method.
|
2791
|
+
|
2792
|
+
```ruby
|
2793
|
+
class Twitter::API < Grape::API
|
2794
|
+
rescue_from :all do |e|
|
2795
|
+
user_id = context.params[:user_id]
|
2796
|
+
error!("error for #{user_id}")
|
2797
|
+
end
|
2798
|
+
end
|
2799
|
+
```
|
2800
|
+
|
2274
2801
|
#### Rescuing exceptions inside namespaces
|
2275
2802
|
|
2276
2803
|
You could put `rescue_from` clauses inside a namespace and they will take precedence over ones
|
@@ -2293,7 +2820,7 @@ class Twitter::API < Grape::API
|
|
2293
2820
|
end
|
2294
2821
|
```
|
2295
2822
|
|
2296
|
-
Here `'inner'` will be result of handling
|
2823
|
+
Here `'inner'` will be result of handling occurred `ArgumentError`.
|
2297
2824
|
|
2298
2825
|
#### Unrescuable Exceptions
|
2299
2826
|
|
@@ -2824,17 +3351,30 @@ end
|
|
2824
3351
|
|
2825
3352
|
Use `body false` to return `204 No Content` without any data or content-type.
|
2826
3353
|
|
2827
|
-
|
3354
|
+
If you want to empty the body with an HTTP status code other than `204 No Content`, you can override the status code after specifying `body false` as follows
|
3355
|
+
|
3356
|
+
```ruby
|
3357
|
+
class API < Grape::API
|
3358
|
+
get '/' do
|
3359
|
+
body false
|
3360
|
+
status 304
|
3361
|
+
end
|
3362
|
+
end
|
3363
|
+
```
|
3364
|
+
|
3365
|
+
You can also set the response to a file with `sendfile`. This works with the
|
3366
|
+
[Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
|
3367
|
+
the file through your web server software.
|
2828
3368
|
|
2829
3369
|
```ruby
|
2830
3370
|
class API < Grape::API
|
2831
3371
|
get '/' do
|
2832
|
-
|
3372
|
+
sendfile '/path/to/file'
|
2833
3373
|
end
|
2834
3374
|
end
|
2835
3375
|
```
|
2836
3376
|
|
2837
|
-
|
3377
|
+
To stream a file in chunks use `stream`
|
2838
3378
|
|
2839
3379
|
```ruby
|
2840
3380
|
class API < Grape::API
|
@@ -2844,6 +3384,26 @@ class API < Grape::API
|
|
2844
3384
|
end
|
2845
3385
|
```
|
2846
3386
|
|
3387
|
+
If you want to stream non-file data use the `stream` method and a `Stream` object.
|
3388
|
+
This is an object that responds to `each` and yields for each chunk to send to the client.
|
3389
|
+
Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
|
3390
|
+
|
3391
|
+
```ruby
|
3392
|
+
class MyStream
|
3393
|
+
def each
|
3394
|
+
yield 'part 1'
|
3395
|
+
yield 'part 2'
|
3396
|
+
yield 'part 3'
|
3397
|
+
end
|
3398
|
+
end
|
3399
|
+
|
3400
|
+
class API < Grape::API
|
3401
|
+
get '/' do
|
3402
|
+
stream MyStream.new
|
3403
|
+
end
|
3404
|
+
end
|
3405
|
+
```
|
3406
|
+
|
2847
3407
|
## Authentication
|
2848
3408
|
|
2849
3409
|
### Basic and Digest Auth
|
@@ -2855,14 +3415,21 @@ applies to the current namespace and any children, but not parents.
|
|
2855
3415
|
```ruby
|
2856
3416
|
http_basic do |username, password|
|
2857
3417
|
# verify user's password here
|
2858
|
-
|
3418
|
+
# IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
|
2859
3419
|
end
|
2860
3420
|
```
|
2861
3421
|
|
3422
|
+
Digest auth supports clear-text passwords and password hashes.
|
3423
|
+
|
2862
3424
|
```ruby
|
2863
3425
|
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
2864
3426
|
# lookup the user's password here
|
2865
|
-
|
3427
|
+
end
|
3428
|
+
```
|
3429
|
+
|
3430
|
+
```ruby
|
3431
|
+
http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
|
3432
|
+
# lookup the user's password hash here
|
2866
3433
|
end
|
2867
3434
|
```
|
2868
3435
|
|
@@ -2893,7 +3460,9 @@ end
|
|
2893
3460
|
|
2894
3461
|
```
|
2895
3462
|
|
2896
|
-
Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
|
3463
|
+
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.
|
3464
|
+
|
3465
|
+
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
3466
|
|
2898
3467
|
## Describing and Inspecting an API
|
2899
3468
|
|
@@ -2962,19 +3531,22 @@ class ApiLogger < Grape::Middleware::Base
|
|
2962
3531
|
end
|
2963
3532
|
```
|
2964
3533
|
|
2965
|
-
## Before and
|
3534
|
+
## Before, After and Finally
|
2966
3535
|
|
2967
3536
|
Blocks can be executed before or after every API call, using `before`, `after`,
|
2968
3537
|
`before_validation` and `after_validation`.
|
3538
|
+
If the API fails the `after` call will not be triggered, if you need code to execute for sure
|
3539
|
+
use the `finally`.
|
2969
3540
|
|
2970
3541
|
Before and after callbacks execute in the following order:
|
2971
3542
|
|
2972
3543
|
1. `before`
|
2973
3544
|
2. `before_validation`
|
2974
3545
|
3. _validations_
|
2975
|
-
4. `after_validation`
|
2976
|
-
5. _the API call_
|
2977
|
-
6. `after`
|
3546
|
+
4. `after_validation` (upon successful validation)
|
3547
|
+
5. _the API call_ (upon successful validation)
|
3548
|
+
6. `after` (upon successful validation and API call)
|
3549
|
+
7. `finally` (always)
|
2978
3550
|
|
2979
3551
|
Steps 4, 5 and 6 only happen if validation succeeds.
|
2980
3552
|
|
@@ -2994,6 +3566,14 @@ before do
|
|
2994
3566
|
end
|
2995
3567
|
```
|
2996
3568
|
|
3569
|
+
You can ensure a block of code runs after every request (including failures) with `finally`:
|
3570
|
+
|
3571
|
+
```ruby
|
3572
|
+
finally do
|
3573
|
+
# this code will run after every request (successful or failed)
|
3574
|
+
end
|
3575
|
+
```
|
3576
|
+
|
2997
3577
|
**Namespaces**
|
2998
3578
|
|
2999
3579
|
Callbacks apply to each API call within and below the current namespace:
|
@@ -3196,11 +3776,21 @@ class API < Grape::API
|
|
3196
3776
|
end
|
3197
3777
|
```
|
3198
3778
|
|
3779
|
+
You can access the controller params, headers, and helpers through the context with the `#context` method inside any middleware inherited from `Grape::Middleware::Base`.
|
3780
|
+
|
3199
3781
|
### Rails Middleware
|
3200
3782
|
|
3201
3783
|
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.
|
3202
3784
|
You only have to implement the helpers to access the specific `env` variable.
|
3203
3785
|
|
3786
|
+
If you are using a custom application that is inherited from `Rails::Application` and need to insert a new middleware among the ones initiated via Rails, you will need to register it manually in your custom application class.
|
3787
|
+
|
3788
|
+
```ruby
|
3789
|
+
class Company::Application < Rails::Application
|
3790
|
+
config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
|
3791
|
+
end
|
3792
|
+
```
|
3793
|
+
|
3204
3794
|
### Remote IP
|
3205
3795
|
|
3206
3796
|
By default you can access remote IP with `request.ip`. This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP [Rails-style](http://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails) with `ActionDispatch::RemoteIp`.
|
@@ -3234,7 +3824,7 @@ Use `rack-test` and define your API as `app`.
|
|
3234
3824
|
You can test a Grape API with RSpec by making HTTP requests and examining the response.
|
3235
3825
|
|
3236
3826
|
```ruby
|
3237
|
-
|
3827
|
+
|
3238
3828
|
|
3239
3829
|
describe Twitter::API do
|
3240
3830
|
include Rack::Test::Methods
|
@@ -3504,6 +4094,8 @@ Grape integrates with following third-party tools:
|
|
3504
4094
|
* **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
|
3505
4095
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
3506
4096
|
* **[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)
|
4097
|
+
* **[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)
|
4098
|
+
* **[Datadog APM](https://docs.datadoghq.com/tracing/)** - [ddtrace](https://github.com/datadog/dd-trace-rb) gem, [documentation](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#grape)
|
3507
4099
|
|
3508
4100
|
## Contributing to Grape
|
3509
4101
|
|
@@ -3512,10 +4104,14 @@ features and discuss issues.
|
|
3512
4104
|
|
3513
4105
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
3514
4106
|
|
4107
|
+
## Security
|
4108
|
+
|
4109
|
+
See [SECURITY](SECURITY.md) for details.
|
4110
|
+
|
3515
4111
|
## License
|
3516
4112
|
|
3517
|
-
MIT License. See LICENSE for details.
|
4113
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
3518
4114
|
|
3519
4115
|
## Copyright
|
3520
4116
|
|
3521
|
-
Copyright (c) 2010-
|
4117
|
+
Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
|