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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -0
  3. data/CONTRIBUTING.md +2 -10
  4. data/README.md +106 -43
  5. data/UPGRADING.md +90 -1
  6. data/grape.gemspec +4 -4
  7. data/lib/grape/api/instance.rb +51 -73
  8. data/lib/grape/api.rb +56 -89
  9. data/lib/grape/cookies.rb +31 -25
  10. data/lib/grape/dry_types.rb +48 -4
  11. data/lib/grape/dsl/callbacks.rb +8 -58
  12. data/lib/grape/dsl/desc.rb +8 -67
  13. data/lib/grape/dsl/headers.rb +1 -1
  14. data/lib/grape/dsl/helpers.rb +60 -65
  15. data/lib/grape/dsl/inside_route.rb +26 -61
  16. data/lib/grape/dsl/logger.rb +3 -6
  17. data/lib/grape/dsl/middleware.rb +22 -40
  18. data/lib/grape/dsl/parameters.rb +10 -19
  19. data/lib/grape/dsl/request_response.rb +136 -139
  20. data/lib/grape/dsl/routing.rb +230 -194
  21. data/lib/grape/dsl/settings.rb +22 -134
  22. data/lib/grape/dsl/validations.rb +37 -45
  23. data/lib/grape/endpoint.rb +91 -126
  24. data/lib/grape/error_formatter/base.rb +2 -0
  25. data/lib/grape/exceptions/base.rb +1 -1
  26. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  27. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  28. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  29. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  30. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  31. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  32. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  33. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
  34. data/lib/grape/extensions/hash.rb +2 -1
  35. data/lib/grape/extensions/hashie/mash.rb +3 -5
  36. data/lib/grape/locale/en.yml +44 -44
  37. data/lib/grape/middleware/auth/base.rb +11 -32
  38. data/lib/grape/middleware/auth/dsl.rb +22 -29
  39. data/lib/grape/middleware/base.rb +30 -11
  40. data/lib/grape/middleware/error.rb +14 -32
  41. data/lib/grape/middleware/formatter.rb +40 -72
  42. data/lib/grape/middleware/stack.rb +28 -38
  43. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -4
  44. data/lib/grape/middleware/versioner/base.rb +30 -56
  45. data/lib/grape/middleware/versioner/header.rb +2 -2
  46. data/lib/grape/middleware/versioner/param.rb +2 -3
  47. data/lib/grape/middleware/versioner/path.rb +1 -1
  48. data/lib/grape/namespace.rb +11 -0
  49. data/lib/grape/params_builder/base.rb +20 -0
  50. data/lib/grape/params_builder/hash.rb +11 -0
  51. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  52. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  53. data/lib/grape/params_builder.rb +32 -0
  54. data/lib/grape/request.rb +161 -22
  55. data/lib/grape/router/route.rb +1 -1
  56. data/lib/grape/router.rb +27 -8
  57. data/lib/grape/util/api_description.rb +56 -0
  58. data/lib/grape/util/base_inheritable.rb +5 -2
  59. data/lib/grape/util/inheritable_setting.rb +7 -0
  60. data/lib/grape/util/media_type.rb +1 -1
  61. data/lib/grape/util/registry.rb +1 -1
  62. data/lib/grape/validations/contract_scope.rb +2 -2
  63. data/lib/grape/validations/params_documentation.rb +50 -0
  64. data/lib/grape/validations/params_scope.rb +46 -56
  65. data/lib/grape/validations/types/array_coercer.rb +2 -3
  66. data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
  67. data/lib/grape/validations/types/primitive_coercer.rb +1 -28
  68. data/lib/grape/validations/types.rb +10 -25
  69. data/lib/grape/validations/validators/base.rb +2 -9
  70. data/lib/grape/validations/validators/except_values_validator.rb +1 -1
  71. data/lib/grape/validations/validators/presence_validator.rb +1 -1
  72. data/lib/grape/validations/validators/regexp_validator.rb +1 -1
  73. data/lib/grape/version.rb +1 -1
  74. data/lib/grape.rb +18 -9
  75. metadata +35 -20
  76. data/lib/grape/api/helpers.rb +0 -9
  77. data/lib/grape/dsl/api.rb +0 -19
  78. data/lib/grape/dsl/configuration.rb +0 -15
  79. data/lib/grape/error_formatter/jsonapi.rb +0 -7
  80. data/lib/grape/http/headers.rb +0 -56
  81. data/lib/grape/middleware/helpers.rb +0 -12
  82. data/lib/grape/parser/jsonapi.rb +0 -7
  83. data/lib/grape/types/invalid_value.rb +0 -8
  84. data/lib/grape/util/lazy/object.rb +0 -45
  85. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  86. data/lib/grape/validations/attributes_doc.rb +0 -60
@@ -3,236 +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
- # Do not route HEAD requests to GET requests automatically.
71
- def do_not_route_head!
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
- # Do not automatically route OPTIONS.
76
- def do_not_route_options!
77
- namespace_inheritable(:do_not_route_options, 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)
78
64
  end
65
+ end
79
66
 
80
- def do_not_document!
81
- namespace_inheritable(:do_not_document, 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 mount(mounts, *opts)
85
- mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
86
- mounts.each_pair do |app, path|
87
- if app.respond_to?(:mount_instance)
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
- if app.respond_to?(:inheritable_setting, true)
95
- mount_path = Grape::Router.normalize_path(path)
96
- app.top_level_setting.namespace_stackable[:mount_path] = mount_path
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
- app.inherit_settings(inheritable_setting)
81
+ def lint!
82
+ inheritable_setting.namespace_inheritable[:lint] = true
83
+ end
99
84
 
100
- in_setting = app.top_level_setting
85
+ def do_not_document!
86
+ inheritable_setting.namespace_inheritable[:do_not_document] = true
87
+ end
101
88
 
102
- app.change!
103
- change!
104
- end
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
- # When trying to mount multiple times the same endpoint, remove the previous ones
107
- # from the list of endpoints if refresh_already_mounted parameter is true
108
- refresh_already_mounted = opts.any? ? opts.first[:refresh_already_mounted] : false
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
- endpoints << Grape::Endpoint.new(
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
- # Defines a route that will be recognized
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
- Grape::Http::Headers::SUPPORTED_METHODS.each do |supported_method|
158
- define_method supported_method.downcase do |*args, &block|
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
- # Declare a "namespace", which prefixes all subordinate routes with its
166
- # name. Any endpoints within a namespace, group, resource or segment,
167
- # etc., will share their parent context as well as any configuration
168
- # done in the namespace context.
169
- #
170
- # @example
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
- alias group namespace
188
- alias resource namespace
189
- alias resources namespace
190
- alias segment namespace
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
- # An array of API routes.
193
- def routes
194
- @routes ||= prepare_routes
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
- # Remove all defined routes.
198
- def reset_routes!
199
- endpoints.each(&:reset_routes!)
200
- @routes = nil
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
- def reset_endpoints!
204
- @endpoints = []
205
- end
195
+ alias group namespace
196
+ alias resource namespace
197
+ alias resources namespace
198
+ alias segment namespace
206
199
 
207
- # This method allows you to quickly define a parameter route segment
208
- # in your API.
209
- #
210
- # @param param [Symbol] The name of the parameter you wish to declare.
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
- options[:requirements] = {
216
- param.to_sym => options[:requirements]
217
- } if options[:requirements].is_a?(Regexp)
205
+ # Remove all defined routes.
206
+ def reset_routes!
207
+ endpoints.each(&:reset_routes!)
208
+ @routes = nil
209
+ end
218
210
 
219
- Grape::Validations::ParamsScope.new(api: self) do
220
- requires param, type: options[:type]
221
- end if options.key?(:type)
211
+ def reset_endpoints!
212
+ @endpoints = []
213
+ end
222
214
 
223
- namespace(":#{param}", options, &block)
224
- end
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
- # @return array of defined versions
227
- def versions
228
- @versions ||= []
229
- end
223
+ options[:requirements] = {
224
+ param.to_sym => options[:requirements]
225
+ } if options[:requirements].is_a?(Regexp)
230
226
 
231
- private
227
+ Grape::Validations::ParamsScope.new(api: self) do
228
+ requires param, type: options[:type]
229
+ end if options.key?(:type)
232
230
 
233
- def refresh_mounted_api(mounts, *opts)
234
- opts << { refresh_already_mounted: true }
235
- mount(mounts, *opts)
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
@@ -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