grape 0.1.1 → 2.4.0
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 +7 -0
- data/CHANGELOG.md +1167 -0
- data/CONTRIBUTING.md +156 -0
- data/LICENSE +1 -1
- data/README.md +4192 -0
- data/UPGRADING.md +1697 -0
- data/grape.gemspec +28 -105
- data/grape.png +0 -0
- data/lib/grape/api/helpers.rb +9 -0
- data/lib/grape/api/instance.rb +247 -0
- data/lib/grape/api.rb +158 -194
- data/lib/grape/content_types.rb +31 -0
- data/lib/grape/cookies.rb +50 -0
- data/lib/grape/dry_types.rb +10 -0
- data/lib/grape/dsl/api.rb +17 -0
- data/lib/grape/dsl/callbacks.rb +69 -0
- data/lib/grape/dsl/configuration.rb +15 -0
- data/lib/grape/dsl/desc.rb +124 -0
- data/lib/grape/dsl/headers.rb +21 -0
- data/lib/grape/dsl/helpers.rb +108 -0
- data/lib/grape/dsl/inside_route.rb +454 -0
- data/lib/grape/dsl/logger.rb +22 -0
- data/lib/grape/dsl/middleware.rb +54 -0
- data/lib/grape/dsl/parameters.rb +266 -0
- data/lib/grape/dsl/request_response.rb +171 -0
- data/lib/grape/dsl/routing.rb +248 -0
- data/lib/grape/dsl/settings.rb +179 -0
- data/lib/grape/dsl/validations.rb +57 -0
- data/lib/grape/endpoint.rb +398 -68
- data/lib/grape/env.rb +20 -0
- data/lib/grape/error_formatter/base.rb +68 -0
- data/lib/grape/error_formatter/json.rb +28 -0
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +21 -0
- data/lib/grape/error_formatter/xml.rb +11 -0
- data/lib/grape/error_formatter.rb +15 -0
- data/lib/grape/exceptions/base.rb +76 -0
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +11 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +11 -0
- data/lib/grape/exceptions/invalid_formatter.rb +11 -0
- data/lib/grape/exceptions/invalid_message_body.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +11 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +11 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +11 -0
- data/lib/grape/exceptions/method_not_allowed.rb +11 -0
- data/lib/grape/exceptions/missing_group_type.rb +13 -0
- data/lib/grape/exceptions/missing_mime_type.rb +11 -0
- data/lib/grape/exceptions/missing_option.rb +11 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_options.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unknown_validator.rb +11 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +13 -0
- data/lib/grape/exceptions/validation.rb +25 -0
- data/lib/grape/exceptions/validation_array_errors.rb +14 -0
- data/lib/grape/exceptions/validation_errors.rb +53 -0
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +24 -0
- data/lib/grape/extensions/hash.rb +27 -0
- data/lib/grape/extensions/hashie/mash.rb +24 -0
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +13 -0
- data/lib/grape/formatter/serializable_hash.rb +39 -0
- data/lib/grape/formatter/txt.rb +11 -0
- data/lib/grape/formatter/xml.rb +13 -0
- data/lib/grape/formatter.rb +17 -0
- data/lib/grape/json.rb +10 -0
- data/lib/grape/locale/en.yml +59 -0
- data/lib/grape/middleware/auth/base.rb +23 -0
- data/lib/grape/middleware/auth/dsl.rb +39 -0
- data/lib/grape/middleware/auth/strategies.rb +25 -0
- data/lib/grape/middleware/auth/strategy_info.rb +15 -0
- data/lib/grape/middleware/base.rb +89 -18
- data/lib/grape/middleware/error.rb +129 -10
- data/lib/grape/middleware/filter.rb +19 -0
- data/lib/grape/middleware/formatter.rb +124 -82
- data/lib/grape/middleware/globals.rb +14 -0
- data/lib/grape/middleware/stack.rb +109 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +38 -0
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +127 -0
- data/lib/grape/middleware/versioner/param.rb +32 -0
- data/lib/grape/middleware/versioner/path.rb +40 -0
- data/lib/grape/middleware/versioner.rb +21 -21
- data/lib/grape/namespace.rb +46 -0
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +14 -0
- data/lib/grape/parser/xml.rb +14 -0
- data/lib/grape/parser.rb +15 -0
- data/lib/grape/path.rb +76 -0
- data/lib/grape/presenters/presenter.rb +11 -0
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +190 -0
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +81 -0
- data/lib/grape/router/route.rb +62 -0
- data/lib/grape/router.rb +190 -0
- data/lib/grape/serve_stream/file_body.rb +36 -0
- data/lib/grape/serve_stream/sendfile_response.rb +21 -0
- data/lib/grape/serve_stream/stream_response.rb +23 -0
- data/lib/grape/types/invalid_value.rb +8 -0
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +17 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +100 -0
- data/lib/grape/util/inheritable_values.rb +29 -0
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +15 -0
- data/lib/grape/util/stackable_values.rb +36 -0
- data/lib/grape/util/strict_hash_configuration.rb +108 -0
- data/lib/grape/validations/attributes_doc.rb +60 -0
- data/lib/grape/validations/attributes_iterator.rb +62 -0
- data/lib/grape/validations/contract_scope.rb +34 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +538 -0
- data/lib/grape/validations/single_attribute_iterator.rb +26 -0
- data/lib/grape/validations/types/array_coercer.rb +61 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +164 -0
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +66 -0
- data/lib/grape/validations/types/file.rb +31 -0
- data/lib/grape/validations/types/invalid_value.rb +17 -0
- data/lib/grape/validations/types/json.rb +71 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +57 -0
- data/lib/grape/validations/types/primitive_coercer.rb +73 -0
- data/lib/grape/validations/types/set_coercer.rb +35 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +51 -0
- data/lib/grape/validations/types.rb +213 -0
- data/lib/grape/validations/validator_factory.rb +15 -0
- 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 +88 -0
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +37 -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/length_validator.rb +49 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +34 -0
- 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 +60 -0
- data/lib/grape/validations.rb +21 -0
- data/lib/grape/version.rb +6 -0
- data/lib/grape/xml.rb +10 -0
- data/lib/grape.rb +81 -17
- metadata +250 -192
- data/.document +0 -5
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.rvmrc +0 -1
- data/Gemfile +0 -21
- data/Gemfile.lock +0 -58
- data/README.markdown +0 -75
- data/Rakefile +0 -70
- data/VERSION +0 -1
- data/autotest/discover.rb +0 -1
- data/lib/grape/middleware/auth/basic.rb +0 -30
- data/lib/grape/middleware/auth/oauth2.rb +0 -55
- data/lib/grape/middleware/prefixer.rb +0 -21
- data/lib/grape/middleware_stack.rb +0 -35
- data/spec/grape/api_spec.rb +0 -343
- data/spec/grape/endpoint_spec.rb +0 -104
- data/spec/grape/middleware/auth/basic_spec.rb +0 -31
- data/spec/grape/middleware/auth/oauth2_spec.rb +0 -88
- data/spec/grape/middleware/base_spec.rb +0 -63
- data/spec/grape/middleware/error_spec.rb +0 -49
- data/spec/grape/middleware/formatter_spec.rb +0 -87
- data/spec/grape/middleware/prefixer_spec.rb +0 -30
- data/spec/grape/middleware/versioner_spec.rb +0 -40
- data/spec/grape/middleware_stack_spec.rb +0 -47
- data/spec/grape_spec.rb +0 -1
- data/spec/spec_helper.rb +0 -20
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DSL
|
|
5
|
+
module Routing
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
include Grape::DSL::Configuration
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
attr_reader :endpoints
|
|
11
|
+
|
|
12
|
+
# Specify an API version.
|
|
13
|
+
#
|
|
14
|
+
# @example API with legacy support.
|
|
15
|
+
# class MyAPI < Grape::API
|
|
16
|
+
# version 'v2'
|
|
17
|
+
#
|
|
18
|
+
# get '/main' do
|
|
19
|
+
# {some: 'data'}
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# version 'v1' do
|
|
23
|
+
# get '/main' do
|
|
24
|
+
# {legacy: 'data'}
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
def version(*args, &block)
|
|
30
|
+
if args.any?
|
|
31
|
+
options = args.extract_options!
|
|
32
|
+
options = options.reverse_merge(using: :path)
|
|
33
|
+
requested_versions = args.flatten.map(&:to_s)
|
|
34
|
+
|
|
35
|
+
raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
|
|
36
|
+
|
|
37
|
+
@versions = versions | requested_versions
|
|
38
|
+
|
|
39
|
+
if block
|
|
40
|
+
within_namespace do
|
|
41
|
+
namespace_inheritable(:version, requested_versions)
|
|
42
|
+
namespace_inheritable(:version_options, options)
|
|
43
|
+
|
|
44
|
+
instance_eval(&block)
|
|
45
|
+
end
|
|
46
|
+
else
|
|
47
|
+
namespace_inheritable(:version, requested_versions)
|
|
48
|
+
namespace_inheritable(:version_options, options)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@versions.last if instance_variable_defined?(:@versions) && @versions
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Define a root URL prefix for your entire API.
|
|
56
|
+
def prefix(prefix = nil)
|
|
57
|
+
namespace_inheritable(:root_prefix, prefix&.to_s)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Create a scope without affecting the URL.
|
|
61
|
+
#
|
|
62
|
+
# @param _name [Symbol] Purely placebo, just allows to name the scope to
|
|
63
|
+
# make the code more readable.
|
|
64
|
+
def scope(_name = nil, &block)
|
|
65
|
+
within_namespace do
|
|
66
|
+
nest(block)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def build_with(build_with)
|
|
71
|
+
namespace_inheritable(:build_params_with, build_with)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Do not route HEAD requests to GET requests automatically.
|
|
75
|
+
def do_not_route_head!
|
|
76
|
+
namespace_inheritable(:do_not_route_head, true)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Do not automatically route OPTIONS.
|
|
80
|
+
def do_not_route_options!
|
|
81
|
+
namespace_inheritable(:do_not_route_options, true)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def lint!
|
|
85
|
+
namespace_inheritable(:lint, true)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def do_not_document!
|
|
89
|
+
namespace_inheritable(:do_not_document, true)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def mount(mounts, *opts)
|
|
93
|
+
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
|
94
|
+
mounts.each_pair do |app, path|
|
|
95
|
+
if app.respond_to?(:mount_instance)
|
|
96
|
+
opts_with = opts.any? ? opts.first[:with] : {}
|
|
97
|
+
mount({ app.mount_instance(configuration: opts_with) => path }, *opts)
|
|
98
|
+
next
|
|
99
|
+
end
|
|
100
|
+
in_setting = inheritable_setting
|
|
101
|
+
|
|
102
|
+
if app.respond_to?(:inheritable_setting, true)
|
|
103
|
+
mount_path = Grape::Router.normalize_path(path)
|
|
104
|
+
app.top_level_setting.namespace_stackable[:mount_path] = mount_path
|
|
105
|
+
|
|
106
|
+
app.inherit_settings(inheritable_setting)
|
|
107
|
+
|
|
108
|
+
in_setting = app.top_level_setting
|
|
109
|
+
|
|
110
|
+
app.change!
|
|
111
|
+
change!
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# When trying to mount multiple times the same endpoint, remove the previous ones
|
|
115
|
+
# from the list of endpoints if refresh_already_mounted parameter is true
|
|
116
|
+
refresh_already_mounted = opts.any? ? opts.first[:refresh_already_mounted] : false
|
|
117
|
+
if refresh_already_mounted && !endpoints.empty?
|
|
118
|
+
endpoints.delete_if do |endpoint|
|
|
119
|
+
endpoint.options[:app].to_s == app.to_s
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
endpoints << Grape::Endpoint.new(
|
|
124
|
+
in_setting,
|
|
125
|
+
method: :any,
|
|
126
|
+
path: path,
|
|
127
|
+
app: app,
|
|
128
|
+
route_options: { anchor: false },
|
|
129
|
+
forward_match: !app.respond_to?(:inheritable_setting),
|
|
130
|
+
for: self
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Defines a route that will be recognized
|
|
136
|
+
# by the Grape API.
|
|
137
|
+
#
|
|
138
|
+
# @param methods [HTTP Verb] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
|
|
139
|
+
# @param paths [String] One or more strings representing the URL segment(s) for this route.
|
|
140
|
+
#
|
|
141
|
+
# @example Defining a basic route.
|
|
142
|
+
# class MyAPI < Grape::API
|
|
143
|
+
# route(:any, '/hello') do
|
|
144
|
+
# {hello: 'world'}
|
|
145
|
+
# end
|
|
146
|
+
# end
|
|
147
|
+
def route(methods, paths = ['/'], route_options = {}, &block)
|
|
148
|
+
methods = '*' if methods == :any
|
|
149
|
+
endpoint_options = {
|
|
150
|
+
method: methods,
|
|
151
|
+
path: paths,
|
|
152
|
+
for: self,
|
|
153
|
+
route_options: {
|
|
154
|
+
params: namespace_stackable_with_hash(:params) || {}
|
|
155
|
+
}.deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
|
|
159
|
+
endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
|
|
160
|
+
|
|
161
|
+
route_end
|
|
162
|
+
reset_validations!
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
Grape::HTTP_SUPPORTED_METHODS.each do |supported_method|
|
|
166
|
+
define_method supported_method.downcase do |*args, &block|
|
|
167
|
+
options = args.extract_options!
|
|
168
|
+
paths = args.first || ['/']
|
|
169
|
+
route(supported_method, paths, options, &block)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Declare a "namespace", which prefixes all subordinate routes with its
|
|
174
|
+
# name. Any endpoints within a namespace, group, resource or segment,
|
|
175
|
+
# etc., will share their parent context as well as any configuration
|
|
176
|
+
# done in the namespace context.
|
|
177
|
+
#
|
|
178
|
+
# @example
|
|
179
|
+
#
|
|
180
|
+
# namespace :foo do
|
|
181
|
+
# get 'bar' do
|
|
182
|
+
# # defines the endpoint: GET /foo/bar
|
|
183
|
+
# end
|
|
184
|
+
# end
|
|
185
|
+
def namespace(space = nil, options = {}, &block)
|
|
186
|
+
return Namespace.joined_space_path(namespace_stackable(:namespace)) unless space || block
|
|
187
|
+
|
|
188
|
+
within_namespace do
|
|
189
|
+
nest(block) do
|
|
190
|
+
namespace_stackable(:namespace, Namespace.new(space, options)) if space
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
alias group namespace
|
|
196
|
+
alias resource namespace
|
|
197
|
+
alias resources namespace
|
|
198
|
+
alias segment namespace
|
|
199
|
+
|
|
200
|
+
# An array of API routes.
|
|
201
|
+
def routes
|
|
202
|
+
@routes ||= prepare_routes
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Remove all defined routes.
|
|
206
|
+
def reset_routes!
|
|
207
|
+
endpoints.each(&:reset_routes!)
|
|
208
|
+
@routes = nil
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def reset_endpoints!
|
|
212
|
+
@endpoints = []
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# This method allows you to quickly define a parameter route segment
|
|
216
|
+
# in your API.
|
|
217
|
+
#
|
|
218
|
+
# @param param [Symbol] The name of the parameter you wish to declare.
|
|
219
|
+
# @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
|
|
220
|
+
def route_param(param, options = {}, &block)
|
|
221
|
+
options = options.dup
|
|
222
|
+
|
|
223
|
+
options[:requirements] = {
|
|
224
|
+
param.to_sym => options[:requirements]
|
|
225
|
+
} if options[:requirements].is_a?(Regexp)
|
|
226
|
+
|
|
227
|
+
Grape::Validations::ParamsScope.new(api: self) do
|
|
228
|
+
requires param, type: options[:type]
|
|
229
|
+
end if options.key?(:type)
|
|
230
|
+
|
|
231
|
+
namespace(":#{param}", options, &block)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# @return array of defined versions
|
|
235
|
+
def versions
|
|
236
|
+
@versions ||= []
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
def refresh_mounted_api(mounts, *opts)
|
|
242
|
+
opts << { refresh_already_mounted: true }
|
|
243
|
+
mount(mounts, *opts)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DSL
|
|
5
|
+
# Keeps track of settings (implemented as key-value pairs, grouped by
|
|
6
|
+
# types), in two contexts: top-level settings which apply globally no
|
|
7
|
+
# matter where they're defined, and inheritable settings which apply only
|
|
8
|
+
# in the current scope and scopes nested under it.
|
|
9
|
+
module Settings
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
attr_writer :inheritable_setting, :top_level_setting
|
|
13
|
+
|
|
14
|
+
# Fetch our top-level settings, which apply to all endpoints in the API.
|
|
15
|
+
def top_level_setting
|
|
16
|
+
@top_level_setting ||= build_top_level_setting
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Fetch our current inheritable settings, which are inherited by
|
|
20
|
+
# nested scopes but not shared across siblings.
|
|
21
|
+
def inheritable_setting
|
|
22
|
+
@inheritable_setting ||= Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from top_level_setting }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param type [Symbol]
|
|
26
|
+
# @param key [Symbol]
|
|
27
|
+
def unset(type, key)
|
|
28
|
+
setting = inheritable_setting.send(type)
|
|
29
|
+
setting.delete key
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param type [Symbol]
|
|
33
|
+
# @param key [Symbol]
|
|
34
|
+
# @param value [Object] will be stored if the value is currently empty
|
|
35
|
+
# @return either the old value, if it wasn't nil, or the given value
|
|
36
|
+
def get_or_set(type, key, value)
|
|
37
|
+
setting = inheritable_setting.send(type)
|
|
38
|
+
if value.nil?
|
|
39
|
+
setting[key]
|
|
40
|
+
else
|
|
41
|
+
setting[key] = value
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @param key [Symbol]
|
|
46
|
+
# @param value [Object]
|
|
47
|
+
# @return (see #get_or_set)
|
|
48
|
+
def global_setting(key, value = nil)
|
|
49
|
+
get_or_set :global, key, value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @param key [Symbol]
|
|
53
|
+
def unset_global_setting(key)
|
|
54
|
+
unset :global, key
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# (see #global_setting)
|
|
58
|
+
def route_setting(key, value = nil)
|
|
59
|
+
get_or_set :route, key, value
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# (see #unset_global_setting)
|
|
63
|
+
def unset_route_setting(key)
|
|
64
|
+
unset :route, key
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# (see #global_setting)
|
|
68
|
+
def namespace_setting(key, value = nil)
|
|
69
|
+
get_or_set :namespace, key, value
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# (see #unset_global_setting)
|
|
73
|
+
def unset_namespace_setting(key)
|
|
74
|
+
unset :namespace, key
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# (see #global_setting)
|
|
78
|
+
def namespace_inheritable(key, value = nil)
|
|
79
|
+
get_or_set :namespace_inheritable, key, value
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# (see #unset_global_setting)
|
|
83
|
+
def unset_namespace_inheritable(key)
|
|
84
|
+
unset :namespace_inheritable, key
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @param key [Symbol]
|
|
88
|
+
def namespace_inheritable_to_nil(key)
|
|
89
|
+
inheritable_setting.namespace_inheritable[key] = nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# (see #global_setting)
|
|
93
|
+
def namespace_stackable(key, value = nil)
|
|
94
|
+
get_or_set :namespace_stackable, key, value
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def namespace_reverse_stackable(key, value = nil)
|
|
98
|
+
get_or_set :namespace_reverse_stackable, key, value
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def namespace_stackable_with_hash(key)
|
|
102
|
+
settings = get_or_set :namespace_stackable, key, nil
|
|
103
|
+
return if settings.blank?
|
|
104
|
+
|
|
105
|
+
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def namespace_reverse_stackable_with_hash(key)
|
|
109
|
+
settings = get_or_set :namespace_reverse_stackable, key, nil
|
|
110
|
+
return if settings.blank?
|
|
111
|
+
|
|
112
|
+
settings.each_with_object({}) do |setting, result|
|
|
113
|
+
result.merge!(setting) { |_k, s1, _s2| s1 }
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# (see #unset_global_setting)
|
|
118
|
+
def unset_namespace_stackable(key)
|
|
119
|
+
unset :namespace_stackable, key
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# (see #global_setting)
|
|
123
|
+
def api_class_setting(key, value = nil)
|
|
124
|
+
get_or_set :api_class, key, value
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# (see #unset_global_setting)
|
|
128
|
+
def unset_api_class_setting(key)
|
|
129
|
+
unset :api_class, key
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Fork our inheritable settings to a new instance, copied from our
|
|
133
|
+
# parent's, but separate so we won't modify it. Every call to this
|
|
134
|
+
# method should have an answering call to #namespace_end.
|
|
135
|
+
def namespace_start
|
|
136
|
+
@inheritable_setting = Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from inheritable_setting }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Set the inheritable settings pointer back up by one level.
|
|
140
|
+
def namespace_end
|
|
141
|
+
route_end
|
|
142
|
+
@inheritable_setting = inheritable_setting.parent
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Stop defining settings for the current route and clear them for the
|
|
146
|
+
# next, within a namespace.
|
|
147
|
+
def route_end
|
|
148
|
+
inheritable_setting.route_end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Execute the block within a context where our inheritable settings are forked
|
|
152
|
+
# to a new copy (see #namespace_start).
|
|
153
|
+
def within_namespace(&block)
|
|
154
|
+
namespace_start
|
|
155
|
+
|
|
156
|
+
result = yield if block
|
|
157
|
+
|
|
158
|
+
namespace_end
|
|
159
|
+
reset_validations!
|
|
160
|
+
|
|
161
|
+
result
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
# Builds the current class :inheritable_setting. If available, it inherits from
|
|
167
|
+
# the superclass's :inheritable_setting.
|
|
168
|
+
def build_top_level_setting
|
|
169
|
+
Grape::Util::InheritableSetting.new.tap do |setting|
|
|
170
|
+
# Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
|
|
171
|
+
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
|
|
172
|
+
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
|
|
173
|
+
# in the chain for every endpoint.
|
|
174
|
+
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module DSL
|
|
5
|
+
module Validations
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
include Grape::DSL::Configuration
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
# Clears all defined parameters and validations. The main purpose of it is to clean up
|
|
12
|
+
# settings, so next endpoint won't interfere with previous one.
|
|
13
|
+
#
|
|
14
|
+
# params do
|
|
15
|
+
# # params for the endpoint below this block
|
|
16
|
+
# end
|
|
17
|
+
# post '/current' do
|
|
18
|
+
# # whatever
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # somewhere between them the reset_validations! method gets called
|
|
22
|
+
#
|
|
23
|
+
# params do
|
|
24
|
+
# # params for the endpoint below this block
|
|
25
|
+
# end
|
|
26
|
+
# post '/next' do
|
|
27
|
+
# # whatever
|
|
28
|
+
# end
|
|
29
|
+
def reset_validations!
|
|
30
|
+
unset_namespace_stackable :declared_params
|
|
31
|
+
unset_namespace_stackable :validations
|
|
32
|
+
unset_namespace_stackable :params
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Opens a root-level ParamsScope, defining parameter coercions and
|
|
36
|
+
# validations for the endpoint.
|
|
37
|
+
# @yield instance context of the new scope
|
|
38
|
+
def params(&block)
|
|
39
|
+
Grape::Validations::ParamsScope.new(api: self, type: Hash, &block)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Declare the contract to be used for the endpoint's parameters.
|
|
43
|
+
# @param contract [Class<Dry::Validation::Contract> | Dry::Schema::Processor]
|
|
44
|
+
# The contract or schema to be used for validation. Optional.
|
|
45
|
+
# @yield a block yielding a new instance of Dry::Schema::Params
|
|
46
|
+
# subclass, allowing to define the schema inline. When the
|
|
47
|
+
# +contract+ parameter is a schema, it will be used as a parent. Optional.
|
|
48
|
+
def contract(contract = nil, &block)
|
|
49
|
+
raise ArgumentError, 'Either contract or block must be provided' unless contract || block
|
|
50
|
+
raise ArgumentError, 'Cannot inherit from contract, only schema' if block && contract.respond_to?(:schema)
|
|
51
|
+
|
|
52
|
+
Grape::Validations::ContractScope.new(self, contract, &block)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|