grape 2.4.0 → 3.0.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 +4 -4
- data/CHANGELOG.md +40 -0
- data/CONTRIBUTING.md +1 -9
- data/README.md +72 -31
- data/UPGRADING.md +34 -0
- data/grape.gemspec +4 -4
- data/lib/grape/api/instance.rb +49 -72
- data/lib/grape/api.rb +24 -34
- data/lib/grape/dry_types.rb +48 -4
- data/lib/grape/dsl/callbacks.rb +8 -58
- data/lib/grape/dsl/desc.rb +8 -67
- data/lib/grape/dsl/helpers.rb +59 -64
- data/lib/grape/dsl/inside_route.rb +20 -43
- data/lib/grape/dsl/logger.rb +3 -6
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +7 -16
- data/lib/grape/dsl/request_response.rb +136 -139
- data/lib/grape/dsl/routing.rb +229 -201
- data/lib/grape/dsl/settings.rb +22 -134
- data/lib/grape/dsl/validations.rb +37 -45
- data/lib/grape/endpoint.rb +64 -96
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/exceptions/base.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +5 -6
- data/lib/grape/middleware/error.rb +1 -11
- data/lib/grape/middleware/formatter.rb +4 -2
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/base.rb +24 -42
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +11 -0
- data/lib/grape/params_builder/base.rb +2 -0
- data/lib/grape/router.rb +4 -3
- data/lib/grape/util/api_description.rb +56 -0
- data/lib/grape/util/base_inheritable.rb +5 -2
- data/lib/grape/util/inheritable_setting.rb +7 -0
- data/lib/grape/util/media_type.rb +1 -1
- data/lib/grape/util/registry.rb +1 -1
- data/lib/grape/validations/contract_scope.rb +2 -2
- data/lib/grape/validations/params_documentation.rb +50 -0
- data/lib/grape/validations/params_scope.rb +38 -53
- data/lib/grape/validations/types/array_coercer.rb +2 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
- data/lib/grape/validations/types/primitive_coercer.rb +1 -28
- data/lib/grape/validations/types.rb +10 -25
- data/lib/grape/validations/validators/base.rb +0 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +7 -10
- metadata +24 -14
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -17
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/validations/attributes_doc.rb +0 -60
data/lib/grape/dsl/routing.rb
CHANGED
|
@@ -3,244 +3,272 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module DSL
|
|
5
5
|
module Routing
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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)
|
|
6
|
+
attr_reader :endpoints
|
|
7
|
+
|
|
8
|
+
# Specify an API version.
|
|
9
|
+
#
|
|
10
|
+
# @example API with legacy support.
|
|
11
|
+
# class MyAPI < Grape::API
|
|
12
|
+
# version 'v2'
|
|
13
|
+
#
|
|
14
|
+
# get '/main' do
|
|
15
|
+
# {some: 'data'}
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# version 'v1' do
|
|
19
|
+
# get '/main' do
|
|
20
|
+
# {legacy: 'data'}
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
def version(*args, **options, &block)
|
|
26
|
+
if args.any?
|
|
27
|
+
options = options.reverse_merge(using: :path)
|
|
28
|
+
requested_versions = args.flatten.map(&:to_s)
|
|
29
|
+
|
|
30
|
+
raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
|
|
31
|
+
|
|
32
|
+
@versions = versions | requested_versions
|
|
33
|
+
|
|
34
|
+
if block
|
|
35
|
+
within_namespace do
|
|
36
|
+
inheritable_setting.namespace_inheritable[:version] = requested_versions
|
|
37
|
+
inheritable_setting.namespace_inheritable[:version_options] = options
|
|
38
|
+
|
|
39
|
+
instance_eval(&block)
|
|
49
40
|
end
|
|
41
|
+
else
|
|
42
|
+
inheritable_setting.namespace_inheritable[:version] = requested_versions
|
|
43
|
+
inheritable_setting.namespace_inheritable[:version_options] = options
|
|
50
44
|
end
|
|
51
|
-
|
|
52
|
-
@versions.last if instance_variable_defined?(:@versions) && @versions
|
|
53
45
|
end
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
namespace_inheritable(:root_prefix, prefix&.to_s)
|
|
58
|
-
end
|
|
47
|
+
@versions.last if instance_variable_defined?(:@versions) && @versions
|
|
48
|
+
end
|
|
59
49
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# make the code more readable.
|
|
64
|
-
def scope(_name = nil, &block)
|
|
65
|
-
within_namespace do
|
|
66
|
-
nest(block)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
50
|
+
# Define a root URL prefix for your entire API.
|
|
51
|
+
def prefix(prefix = nil)
|
|
52
|
+
return inheritable_setting.namespace_inheritable[:root_prefix] if prefix.nil?
|
|
69
53
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
54
|
+
inheritable_setting.namespace_inheritable[:root_prefix] = prefix.to_s
|
|
55
|
+
end
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
# Create a scope without affecting the URL.
|
|
58
|
+
#
|
|
59
|
+
# @param _name [Symbol] Purely placebo, just allows to name the scope to
|
|
60
|
+
# make the code more readable.
|
|
61
|
+
def scope(_name = nil, &block)
|
|
62
|
+
within_namespace do
|
|
63
|
+
nest(block)
|
|
77
64
|
end
|
|
65
|
+
end
|
|
78
66
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
end
|
|
67
|
+
def build_with(build_with)
|
|
68
|
+
inheritable_setting.namespace_inheritable[:build_params_with] = build_with
|
|
69
|
+
end
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
# Do not route HEAD requests to GET requests automatically.
|
|
72
|
+
def do_not_route_head!
|
|
73
|
+
inheritable_setting.namespace_inheritable[:do_not_route_head] = true
|
|
74
|
+
end
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
76
|
+
# Do not automatically route OPTIONS.
|
|
77
|
+
def do_not_route_options!
|
|
78
|
+
inheritable_setting.namespace_inheritable[:do_not_route_options] = true
|
|
79
|
+
end
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
81
|
+
def lint!
|
|
82
|
+
inheritable_setting.namespace_inheritable[:lint] = true
|
|
83
|
+
end
|
|
101
84
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
def do_not_document!
|
|
86
|
+
inheritable_setting.namespace_inheritable[:do_not_document] = true
|
|
87
|
+
end
|
|
105
88
|
|
|
106
|
-
|
|
89
|
+
def mount(mounts, *opts)
|
|
90
|
+
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
|
91
|
+
mounts.each_pair do |app, path|
|
|
92
|
+
if app.respond_to?(:mount_instance)
|
|
93
|
+
opts_with = opts.any? ? opts.first[:with] : {}
|
|
94
|
+
mount({ app.mount_instance(configuration: opts_with) => path }, *opts)
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
in_setting = inheritable_setting
|
|
107
98
|
|
|
108
|
-
|
|
99
|
+
if app.respond_to?(:inheritable_setting, true)
|
|
100
|
+
mount_path = Grape::Router.normalize_path(path)
|
|
101
|
+
app.top_level_setting.namespace_stackable[:mount_path] = mount_path
|
|
109
102
|
|
|
110
|
-
|
|
111
|
-
change!
|
|
112
|
-
end
|
|
103
|
+
app.inherit_settings(inheritable_setting)
|
|
113
104
|
|
|
114
|
-
|
|
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
|
|
105
|
+
in_setting = app.top_level_setting
|
|
122
106
|
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
)
|
|
107
|
+
app.change!
|
|
108
|
+
change!
|
|
132
109
|
end
|
|
133
|
-
end
|
|
134
110
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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!
|
|
111
|
+
# When trying to mount multiple times the same endpoint, remove the previous ones
|
|
112
|
+
# from the list of endpoints if refresh_already_mounted parameter is true
|
|
113
|
+
refresh_already_mounted = opts.any? ? opts.first[:refresh_already_mounted] : false
|
|
114
|
+
if refresh_already_mounted && !endpoints.empty?
|
|
115
|
+
endpoints.delete_if do |endpoint|
|
|
116
|
+
endpoint.options[:app].to_s == app.to_s
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
endpoints << Grape::Endpoint.new(
|
|
121
|
+
in_setting,
|
|
122
|
+
method: :any,
|
|
123
|
+
path: path,
|
|
124
|
+
app: app,
|
|
125
|
+
route_options: { anchor: false },
|
|
126
|
+
forward_match: !app.respond_to?(:inheritable_setting),
|
|
127
|
+
for: self
|
|
128
|
+
)
|
|
163
129
|
end
|
|
130
|
+
end
|
|
164
131
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
132
|
+
# Defines a route that will be recognized
|
|
133
|
+
# by the Grape API.
|
|
134
|
+
#
|
|
135
|
+
# @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.
|
|
136
|
+
# @param paths [String] One or more strings representing the URL segment(s) for this route.
|
|
137
|
+
#
|
|
138
|
+
# @example Defining a basic route.
|
|
139
|
+
# class MyAPI < Grape::API
|
|
140
|
+
# route(:any, '/hello') do
|
|
141
|
+
# {hello: 'world'}
|
|
142
|
+
# end
|
|
143
|
+
# end
|
|
144
|
+
def route(methods, paths = ['/'], route_options = {}, &block)
|
|
145
|
+
method = methods == :any ? '*' : methods
|
|
146
|
+
endpoint_params = inheritable_setting.namespace_stackable_with_hash(:params) || {}
|
|
147
|
+
endpoint_description = inheritable_setting.route[:description]
|
|
148
|
+
all_route_options = { params: endpoint_params }
|
|
149
|
+
all_route_options.deep_merge!(endpoint_description) if endpoint_description
|
|
150
|
+
all_route_options.deep_merge!(route_options) if route_options&.any?
|
|
151
|
+
|
|
152
|
+
endpoint_options = {
|
|
153
|
+
method: method,
|
|
154
|
+
path: paths,
|
|
155
|
+
for: self,
|
|
156
|
+
route_options: all_route_options
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
|
|
160
|
+
endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
|
|
161
|
+
|
|
162
|
+
inheritable_setting.route_end
|
|
163
|
+
reset_validations!
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
Grape::HTTP_SUPPORTED_METHODS.each do |supported_method|
|
|
167
|
+
define_method supported_method.downcase do |*args, **options, &block|
|
|
168
|
+
paths = args.first || ['/']
|
|
169
|
+
route(supported_method, paths, options, &block)
|
|
171
170
|
end
|
|
171
|
+
end
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
end
|
|
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(inheritable_setting.namespace_stackable[:namespace]) unless space || block
|
|
187
|
+
|
|
188
|
+
within_namespace do
|
|
189
|
+
nest(block) do
|
|
190
|
+
inheritable_setting.namespace_stackable[:namespace] = Grape::Namespace.new(space, options) if space
|
|
192
191
|
end
|
|
193
192
|
end
|
|
193
|
+
end
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
alias group namespace
|
|
196
|
+
alias resource namespace
|
|
197
|
+
alias resources namespace
|
|
198
|
+
alias segment namespace
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
# An array of API routes.
|
|
201
|
+
def routes
|
|
202
|
+
@routes ||= endpoints.map(&:routes).flatten
|
|
203
|
+
end
|
|
204
204
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
205
|
+
# Remove all defined routes.
|
|
206
|
+
def reset_routes!
|
|
207
|
+
endpoints.each(&:reset_routes!)
|
|
208
|
+
@routes = nil
|
|
209
|
+
end
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
def reset_endpoints!
|
|
212
|
+
@endpoints = []
|
|
213
|
+
end
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
options[:requirements] = {
|
|
224
|
+
param.to_sym => options[:requirements]
|
|
225
|
+
} if options[:requirements].is_a?(Regexp)
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
Grape::Validations::ParamsScope.new(api: self) do
|
|
228
|
+
requires param, type: options[:type]
|
|
229
|
+
end if options.key?(:type)
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
namespace(":#{param}", options, &block)
|
|
232
|
+
end
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
# @return array of defined versions
|
|
235
|
+
def versions
|
|
236
|
+
@versions ||= []
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
238
240
|
|
|
239
|
-
|
|
241
|
+
def refresh_mounted_api(mounts, *opts)
|
|
242
|
+
opts << { refresh_already_mounted: true }
|
|
243
|
+
mount(mounts, *opts)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Execute first the provided block, then each of the
|
|
247
|
+
# block passed in. Allows for simple 'before' setups
|
|
248
|
+
# of settings stack pushes.
|
|
249
|
+
def nest(*blocks, &block)
|
|
250
|
+
blocks.compact!
|
|
251
|
+
if blocks.any?
|
|
252
|
+
evaluate_as_instance_with_configuration(block) if block
|
|
253
|
+
blocks.each { |b| evaluate_as_instance_with_configuration(b) }
|
|
254
|
+
reset_validations!
|
|
255
|
+
else
|
|
256
|
+
instance_eval(&block)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
240
259
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
260
|
+
def evaluate_as_instance_with_configuration(block, lazy: false)
|
|
261
|
+
lazy_block = Grape::Util::Lazy::Block.new do |configuration|
|
|
262
|
+
value_for_configuration = configuration
|
|
263
|
+
self.configuration = value_for_configuration.evaluate if value_for_configuration.try(:lazy?)
|
|
264
|
+
response = instance_eval(&block)
|
|
265
|
+
self.configuration = value_for_configuration
|
|
266
|
+
response
|
|
267
|
+
end
|
|
268
|
+
if base && base_instance? && lazy
|
|
269
|
+
lazy_block
|
|
270
|
+
else
|
|
271
|
+
lazy_block.evaluate_from(configuration)
|
|
244
272
|
end
|
|
245
273
|
end
|
|
246
274
|
end
|
data/lib/grape/dsl/settings.rb
CHANGED
|
@@ -7,13 +7,17 @@ module Grape
|
|
|
7
7
|
# matter where they're defined, and inheritable settings which apply only
|
|
8
8
|
# in the current scope and scopes nested under it.
|
|
9
9
|
module Settings
|
|
10
|
-
extend ActiveSupport::Concern
|
|
11
|
-
|
|
12
10
|
attr_writer :inheritable_setting, :top_level_setting
|
|
13
11
|
|
|
14
12
|
# Fetch our top-level settings, which apply to all endpoints in the API.
|
|
15
13
|
def top_level_setting
|
|
16
|
-
@top_level_setting ||=
|
|
14
|
+
@top_level_setting ||= Grape::Util::InheritableSetting.new.tap do |setting|
|
|
15
|
+
# Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
|
|
16
|
+
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
|
|
17
|
+
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
|
|
18
|
+
# in the chain for every endpoint.
|
|
19
|
+
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
|
20
|
+
end
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
# Fetch our current inheritable settings, which are inherited by
|
|
@@ -22,157 +26,41 @@ module Grape
|
|
|
22
26
|
@inheritable_setting ||= Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from top_level_setting }
|
|
23
27
|
end
|
|
24
28
|
|
|
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
29
|
def global_setting(key, value = nil)
|
|
49
|
-
get_or_set
|
|
30
|
+
get_or_set(inheritable_setting.global, key, value)
|
|
50
31
|
end
|
|
51
32
|
|
|
52
|
-
# @param key [Symbol]
|
|
53
|
-
def unset_global_setting(key)
|
|
54
|
-
unset :global, key
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# (see #global_setting)
|
|
58
33
|
def route_setting(key, value = nil)
|
|
59
|
-
get_or_set
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# (see #unset_global_setting)
|
|
63
|
-
def unset_route_setting(key)
|
|
64
|
-
unset :route, key
|
|
34
|
+
get_or_set(inheritable_setting.route, key, value)
|
|
65
35
|
end
|
|
66
36
|
|
|
67
|
-
# (see #global_setting)
|
|
68
37
|
def namespace_setting(key, value = nil)
|
|
69
|
-
get_or_set
|
|
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
|
|
38
|
+
get_or_set(inheritable_setting.namespace, key, value)
|
|
85
39
|
end
|
|
86
40
|
|
|
87
|
-
|
|
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
|
|
41
|
+
private
|
|
150
42
|
|
|
151
43
|
# Execute the block within a context where our inheritable settings are forked
|
|
152
44
|
# to a new copy (see #namespace_start).
|
|
153
|
-
def within_namespace
|
|
154
|
-
|
|
45
|
+
def within_namespace
|
|
46
|
+
new_inheritable_settings = Grape::Util::InheritableSetting.new
|
|
47
|
+
new_inheritable_settings.inherit_from inheritable_setting
|
|
155
48
|
|
|
156
|
-
|
|
49
|
+
@inheritable_setting = new_inheritable_settings
|
|
157
50
|
|
|
158
|
-
|
|
51
|
+
result = yield
|
|
52
|
+
|
|
53
|
+
inheritable_setting.route_end
|
|
54
|
+
@inheritable_setting = inheritable_setting.parent
|
|
159
55
|
reset_validations!
|
|
160
56
|
|
|
161
57
|
result
|
|
162
58
|
end
|
|
163
59
|
|
|
164
|
-
|
|
60
|
+
def get_or_set(setting, key, value)
|
|
61
|
+
return setting[key] if value.nil?
|
|
165
62
|
|
|
166
|
-
|
|
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
|
|
63
|
+
setting[key] = value
|
|
176
64
|
end
|
|
177
65
|
end
|
|
178
66
|
end
|