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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/CONTRIBUTING.md +1 -9
  4. data/README.md +72 -31
  5. data/UPGRADING.md +34 -0
  6. data/grape.gemspec +4 -4
  7. data/lib/grape/api/instance.rb +49 -72
  8. data/lib/grape/api.rb +24 -34
  9. data/lib/grape/dry_types.rb +48 -4
  10. data/lib/grape/dsl/callbacks.rb +8 -58
  11. data/lib/grape/dsl/desc.rb +8 -67
  12. data/lib/grape/dsl/helpers.rb +59 -64
  13. data/lib/grape/dsl/inside_route.rb +20 -43
  14. data/lib/grape/dsl/logger.rb +3 -6
  15. data/lib/grape/dsl/middleware.rb +22 -40
  16. data/lib/grape/dsl/parameters.rb +7 -16
  17. data/lib/grape/dsl/request_response.rb +136 -139
  18. data/lib/grape/dsl/routing.rb +229 -201
  19. data/lib/grape/dsl/settings.rb +22 -134
  20. data/lib/grape/dsl/validations.rb +37 -45
  21. data/lib/grape/endpoint.rb +64 -96
  22. data/lib/grape/error_formatter/base.rb +2 -0
  23. data/lib/grape/exceptions/base.rb +1 -1
  24. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  25. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  26. data/lib/grape/middleware/auth/dsl.rb +5 -6
  27. data/lib/grape/middleware/error.rb +1 -11
  28. data/lib/grape/middleware/formatter.rb +4 -2
  29. data/lib/grape/middleware/stack.rb +2 -2
  30. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  31. data/lib/grape/middleware/versioner/base.rb +24 -42
  32. data/lib/grape/middleware/versioner/header.rb +1 -1
  33. data/lib/grape/middleware/versioner/param.rb +2 -2
  34. data/lib/grape/middleware/versioner/path.rb +1 -1
  35. data/lib/grape/namespace.rb +11 -0
  36. data/lib/grape/params_builder/base.rb +2 -0
  37. data/lib/grape/router.rb +4 -3
  38. data/lib/grape/util/api_description.rb +56 -0
  39. data/lib/grape/util/base_inheritable.rb +5 -2
  40. data/lib/grape/util/inheritable_setting.rb +7 -0
  41. data/lib/grape/util/media_type.rb +1 -1
  42. data/lib/grape/util/registry.rb +1 -1
  43. data/lib/grape/validations/contract_scope.rb +2 -2
  44. data/lib/grape/validations/params_documentation.rb +50 -0
  45. data/lib/grape/validations/params_scope.rb +38 -53
  46. data/lib/grape/validations/types/array_coercer.rb +2 -3
  47. data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
  48. data/lib/grape/validations/types/primitive_coercer.rb +1 -28
  49. data/lib/grape/validations/types.rb +10 -25
  50. data/lib/grape/validations/validators/base.rb +0 -7
  51. data/lib/grape/version.rb +1 -1
  52. data/lib/grape.rb +7 -10
  53. metadata +24 -14
  54. data/lib/grape/api/helpers.rb +0 -9
  55. data/lib/grape/dsl/api.rb +0 -17
  56. data/lib/grape/dsl/configuration.rb +0 -15
  57. data/lib/grape/types/invalid_value.rb +0 -8
  58. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  59. data/lib/grape/validations/attributes_doc.rb +0 -60
@@ -3,244 +3,272 @@
3
3
  module Grape
4
4
  module DSL
5
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)
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
- # Define a root URL prefix for your entire API.
56
- def prefix(prefix = nil)
57
- namespace_inheritable(:root_prefix, prefix&.to_s)
58
- end
47
+ @versions.last if instance_variable_defined?(:@versions) && @versions
48
+ end
59
49
 
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
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
- def build_with(build_with)
71
- namespace_inheritable(:build_params_with, build_with)
72
- end
54
+ inheritable_setting.namespace_inheritable[:root_prefix] = prefix.to_s
55
+ end
73
56
 
74
- # Do not route HEAD requests to GET requests automatically.
75
- def do_not_route_head!
76
- namespace_inheritable(:do_not_route_head, true)
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
- # Do not automatically route OPTIONS.
80
- def do_not_route_options!
81
- namespace_inheritable(:do_not_route_options, true)
82
- end
67
+ def build_with(build_with)
68
+ inheritable_setting.namespace_inheritable[:build_params_with] = build_with
69
+ end
83
70
 
84
- def lint!
85
- namespace_inheritable(:lint, true)
86
- end
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
- def do_not_document!
89
- namespace_inheritable(:do_not_document, true)
90
- end
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
- 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
81
+ def lint!
82
+ inheritable_setting.namespace_inheritable[:lint] = true
83
+ end
101
84
 
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
85
+ def do_not_document!
86
+ inheritable_setting.namespace_inheritable[:do_not_document] = true
87
+ end
105
88
 
106
- app.inherit_settings(inheritable_setting)
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
- in_setting = app.top_level_setting
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
- app.change!
111
- change!
112
- end
103
+ app.inherit_settings(inheritable_setting)
113
104
 
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
105
+ in_setting = app.top_level_setting
122
106
 
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
- )
107
+ app.change!
108
+ change!
132
109
  end
133
- end
134
110
 
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!
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
- 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
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
- # 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
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
- alias group namespace
196
- alias resource namespace
197
- alias resources namespace
198
- alias segment namespace
195
+ alias group namespace
196
+ alias resource namespace
197
+ alias resources namespace
198
+ alias segment namespace
199
199
 
200
- # An array of API routes.
201
- def routes
202
- @routes ||= prepare_routes
203
- end
200
+ # An array of API routes.
201
+ def routes
202
+ @routes ||= endpoints.map(&:routes).flatten
203
+ end
204
204
 
205
- # Remove all defined routes.
206
- def reset_routes!
207
- endpoints.each(&:reset_routes!)
208
- @routes = nil
209
- end
205
+ # Remove all defined routes.
206
+ def reset_routes!
207
+ endpoints.each(&:reset_routes!)
208
+ @routes = nil
209
+ end
210
210
 
211
- def reset_endpoints!
212
- @endpoints = []
213
- end
211
+ def reset_endpoints!
212
+ @endpoints = []
213
+ end
214
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
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
- options[:requirements] = {
224
- param.to_sym => options[:requirements]
225
- } if options[:requirements].is_a?(Regexp)
223
+ options[:requirements] = {
224
+ param.to_sym => options[:requirements]
225
+ } if options[:requirements].is_a?(Regexp)
226
226
 
227
- Grape::Validations::ParamsScope.new(api: self) do
228
- requires param, type: options[:type]
229
- end if options.key?(:type)
227
+ Grape::Validations::ParamsScope.new(api: self) do
228
+ requires param, type: options[:type]
229
+ end if options.key?(:type)
230
230
 
231
- namespace(":#{param}", options, &block)
232
- end
231
+ namespace(":#{param}", options, &block)
232
+ end
233
233
 
234
- # @return array of defined versions
235
- def versions
236
- @versions ||= []
237
- end
234
+ # @return array of defined versions
235
+ def versions
236
+ @versions ||= []
237
+ end
238
+
239
+ private
238
240
 
239
- private
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
- def refresh_mounted_api(mounts, *opts)
242
- opts << { refresh_already_mounted: true }
243
- mount(mounts, *opts)
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
@@ -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 ||= build_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 :global, key, value
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 :route, key, value
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 :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
38
+ get_or_set(inheritable_setting.namespace, key, value)
85
39
  end
86
40
 
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
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(&block)
154
- namespace_start
45
+ def within_namespace
46
+ new_inheritable_settings = Grape::Util::InheritableSetting.new
47
+ new_inheritable_settings.inherit_from inheritable_setting
155
48
 
156
- result = yield if block
49
+ @inheritable_setting = new_inheritable_settings
157
50
 
158
- namespace_end
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
- private
60
+ def get_or_set(setting, key, value)
61
+ return setting[key] if value.nil?
165
62
 
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
63
+ setting[key] = value
176
64
  end
177
65
  end
178
66
  end