grape 2.3.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 +69 -0
- data/CONTRIBUTING.md +2 -10
- data/README.md +106 -43
- data/UPGRADING.md +90 -1
- data/grape.gemspec +4 -4
- data/lib/grape/api/instance.rb +51 -73
- data/lib/grape/api.rb +56 -89
- data/lib/grape/cookies.rb +31 -25
- 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/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +60 -65
- data/lib/grape/dsl/inside_route.rb +26 -61
- data/lib/grape/dsl/logger.rb +3 -6
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +10 -19
- data/lib/grape/dsl/request_response.rb +136 -139
- data/lib/grape/dsl/routing.rb +230 -194
- data/lib/grape/dsl/settings.rb +22 -134
- data/lib/grape/dsl/validations.rb +37 -45
- data/lib/grape/endpoint.rb +91 -126
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/exceptions/base.rb +1 -1
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +2 -1
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/locale/en.yml +44 -44
- data/lib/grape/middleware/auth/base.rb +11 -32
- data/lib/grape/middleware/auth/dsl.rb +22 -29
- data/lib/grape/middleware/base.rb +30 -11
- data/lib/grape/middleware/error.rb +14 -32
- data/lib/grape/middleware/formatter.rb +40 -72
- data/lib/grape/middleware/stack.rb +28 -38
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -4
- data/lib/grape/middleware/versioner/base.rb +30 -56
- data/lib/grape/middleware/versioner/header.rb +2 -2
- data/lib/grape/middleware/versioner/param.rb +2 -3
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +11 -0
- data/lib/grape/params_builder/base.rb +20 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/request.rb +161 -22
- data/lib/grape/router/route.rb +1 -1
- data/lib/grape/router.rb +27 -8
- 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 +46 -56
- 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 +2 -9
- data/lib/grape/validations/validators/except_values_validator.rb +1 -1
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +18 -9
- metadata +35 -20
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -19
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/error_formatter/jsonapi.rb +0 -7
- data/lib/grape/http/headers.rb +0 -56
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/parser/jsonapi.rb +0 -7
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/lazy/object.rb +0 -45
- 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,236 +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
|
-
namespace_inheritable(:do_not_route_head, true)
|
|
73
|
-
end
|
|
54
|
+
inheritable_setting.namespace_inheritable[:root_prefix] = prefix.to_s
|
|
55
|
+
end
|
|
74
56
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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)
|
|
78
64
|
end
|
|
65
|
+
end
|
|
79
66
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
67
|
+
def build_with(build_with)
|
|
68
|
+
inheritable_setting.namespace_inheritable[:build_params_with] = build_with
|
|
69
|
+
end
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
opts_with = opts.any? ? opts.first[:with] : {}
|
|
89
|
-
mount({ app.mount_instance(configuration: opts_with) => path }, *opts)
|
|
90
|
-
next
|
|
91
|
-
end
|
|
92
|
-
in_setting = inheritable_setting
|
|
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
|
|
93
75
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
# Do not automatically route OPTIONS.
|
|
77
|
+
def do_not_route_options!
|
|
78
|
+
inheritable_setting.namespace_inheritable[:do_not_route_options] = true
|
|
79
|
+
end
|
|
97
80
|
|
|
98
|
-
|
|
81
|
+
def lint!
|
|
82
|
+
inheritable_setting.namespace_inheritable[:lint] = true
|
|
83
|
+
end
|
|
99
84
|
|
|
100
|
-
|
|
85
|
+
def do_not_document!
|
|
86
|
+
inheritable_setting.namespace_inheritable[:do_not_document] = true
|
|
87
|
+
end
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if refresh_already_mounted && !endpoints.empty?
|
|
110
|
-
endpoints.delete_if do |endpoint|
|
|
111
|
-
endpoint.options[:app].to_s == app.to_s
|
|
112
|
-
end
|
|
113
|
-
end
|
|
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
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
in_setting,
|
|
117
|
-
method: :any,
|
|
118
|
-
path: path,
|
|
119
|
-
app: app,
|
|
120
|
-
route_options: { anchor: false },
|
|
121
|
-
forward_match: !app.respond_to?(:inheritable_setting),
|
|
122
|
-
for: self
|
|
123
|
-
)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
103
|
+
app.inherit_settings(inheritable_setting)
|
|
126
104
|
|
|
127
|
-
|
|
128
|
-
# by the Grape API.
|
|
129
|
-
#
|
|
130
|
-
# @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.
|
|
131
|
-
# @param paths [String] One or more strings representing the URL segment(s) for this route.
|
|
132
|
-
#
|
|
133
|
-
# @example Defining a basic route.
|
|
134
|
-
# class MyAPI < Grape::API
|
|
135
|
-
# route(:any, '/hello') do
|
|
136
|
-
# {hello: 'world'}
|
|
137
|
-
# end
|
|
138
|
-
# end
|
|
139
|
-
def route(methods, paths = ['/'], route_options = {}, &block)
|
|
140
|
-
methods = '*' if methods == :any
|
|
141
|
-
endpoint_options = {
|
|
142
|
-
method: methods,
|
|
143
|
-
path: paths,
|
|
144
|
-
for: self,
|
|
145
|
-
route_options: {
|
|
146
|
-
params: namespace_stackable_with_hash(:params) || {}
|
|
147
|
-
}.deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
|
|
151
|
-
endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
|
|
152
|
-
|
|
153
|
-
route_end
|
|
154
|
-
reset_validations!
|
|
155
|
-
end
|
|
105
|
+
in_setting = app.top_level_setting
|
|
156
106
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
options = args.extract_options!
|
|
160
|
-
paths = args.first || ['/']
|
|
161
|
-
route(supported_method, paths, options, &block)
|
|
107
|
+
app.change!
|
|
108
|
+
change!
|
|
162
109
|
end
|
|
163
|
-
end
|
|
164
110
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
#
|
|
172
|
-
# namespace :foo do
|
|
173
|
-
# get 'bar' do
|
|
174
|
-
# # defines the endpoint: GET /foo/bar
|
|
175
|
-
# end
|
|
176
|
-
# end
|
|
177
|
-
def namespace(space = nil, options = {}, &block)
|
|
178
|
-
return Namespace.joined_space_path(namespace_stackable(:namespace)) unless space || block
|
|
179
|
-
|
|
180
|
-
within_namespace do
|
|
181
|
-
nest(block) do
|
|
182
|
-
namespace_stackable(:namespace, Namespace.new(space, options)) if space
|
|
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
|
|
183
117
|
end
|
|
184
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
|
+
)
|
|
185
129
|
end
|
|
130
|
+
end
|
|
186
131
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
191
165
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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)
|
|
195
170
|
end
|
|
171
|
+
end
|
|
196
172
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|
191
|
+
end
|
|
201
192
|
end
|
|
193
|
+
end
|
|
202
194
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
195
|
+
alias group namespace
|
|
196
|
+
alias resource namespace
|
|
197
|
+
alias resources namespace
|
|
198
|
+
alias segment namespace
|
|
206
199
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
# @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
|
|
212
|
-
def route_param(param, options = {}, &block)
|
|
213
|
-
options = options.dup
|
|
200
|
+
# An array of API routes.
|
|
201
|
+
def routes
|
|
202
|
+
@routes ||= endpoints.map(&:routes).flatten
|
|
203
|
+
end
|
|
214
204
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
205
|
+
# Remove all defined routes.
|
|
206
|
+
def reset_routes!
|
|
207
|
+
endpoints.each(&:reset_routes!)
|
|
208
|
+
@routes = nil
|
|
209
|
+
end
|
|
218
210
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
def reset_endpoints!
|
|
212
|
+
@endpoints = []
|
|
213
|
+
end
|
|
222
214
|
|
|
223
|
-
|
|
224
|
-
|
|
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
|
|
225
222
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
end
|
|
223
|
+
options[:requirements] = {
|
|
224
|
+
param.to_sym => options[:requirements]
|
|
225
|
+
} if options[:requirements].is_a?(Regexp)
|
|
230
226
|
|
|
231
|
-
|
|
227
|
+
Grape::Validations::ParamsScope.new(api: self) do
|
|
228
|
+
requires param, type: options[:type]
|
|
229
|
+
end if options.key?(:type)
|
|
232
230
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
+
|
|
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
|
|
259
|
+
|
|
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)
|
|
236
272
|
end
|
|
237
273
|
end
|
|
238
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
|