grape 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +41 -18
  5. data/UPGRADING.md +75 -1
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +25 -60
  8. data/lib/grape/api.rb +44 -76
  9. data/lib/grape/cookies.rb +31 -25
  10. data/lib/grape/dsl/api.rb +0 -2
  11. data/lib/grape/dsl/desc.rb +27 -24
  12. data/lib/grape/dsl/headers.rb +1 -1
  13. data/lib/grape/dsl/helpers.rb +1 -1
  14. data/lib/grape/dsl/inside_route.rb +17 -40
  15. data/lib/grape/dsl/parameters.rb +5 -5
  16. data/lib/grape/dsl/routing.rb +14 -13
  17. data/lib/grape/endpoint.rb +100 -106
  18. data/lib/grape/error_formatter/base.rb +51 -21
  19. data/lib/grape/error_formatter/json.rb +7 -24
  20. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  21. data/lib/grape/error_formatter/txt.rb +13 -20
  22. data/lib/grape/error_formatter/xml.rb +3 -13
  23. data/lib/grape/error_formatter.rb +4 -12
  24. data/lib/grape/exceptions/base.rb +18 -30
  25. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  26. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  27. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  28. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  29. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  30. data/lib/grape/exceptions/validation.rb +5 -4
  31. data/lib/grape/exceptions/validation_errors.rb +2 -2
  32. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
  33. data/lib/grape/extensions/hash.rb +2 -1
  34. data/lib/grape/extensions/hashie/mash.rb +3 -5
  35. data/lib/grape/formatter/base.rb +16 -0
  36. data/lib/grape/formatter/json.rb +4 -6
  37. data/lib/grape/formatter/serializable_hash.rb +1 -1
  38. data/lib/grape/formatter/txt.rb +3 -5
  39. data/lib/grape/formatter/xml.rb +4 -6
  40. data/lib/grape/formatter.rb +4 -12
  41. data/lib/grape/locale/en.yml +44 -44
  42. data/lib/grape/middleware/auth/base.rb +11 -32
  43. data/lib/grape/middleware/auth/dsl.rb +23 -29
  44. data/lib/grape/middleware/base.rb +30 -11
  45. data/lib/grape/middleware/error.rb +18 -24
  46. data/lib/grape/middleware/formatter.rb +39 -73
  47. data/lib/grape/middleware/stack.rb +26 -36
  48. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
  49. data/lib/grape/middleware/versioner/base.rb +74 -0
  50. data/lib/grape/middleware/versioner/header.rb +4 -10
  51. data/lib/grape/middleware/versioner/param.rb +2 -5
  52. data/lib/grape/middleware/versioner/path.rb +0 -2
  53. data/lib/grape/middleware/versioner.rb +5 -3
  54. data/lib/grape/namespace.rb +1 -1
  55. data/lib/grape/params_builder/base.rb +18 -0
  56. data/lib/grape/params_builder/hash.rb +11 -0
  57. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  58. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  59. data/lib/grape/params_builder.rb +32 -0
  60. data/lib/grape/parser/base.rb +16 -0
  61. data/lib/grape/parser/json.rb +6 -8
  62. data/lib/grape/parser/xml.rb +6 -8
  63. data/lib/grape/parser.rb +5 -7
  64. data/lib/grape/path.rb +39 -56
  65. data/lib/grape/request.rb +162 -23
  66. data/lib/grape/router/base_route.rb +2 -2
  67. data/lib/grape/router/greedy_route.rb +2 -2
  68. data/lib/grape/router/pattern.rb +23 -18
  69. data/lib/grape/router/route.rb +14 -6
  70. data/lib/grape/router.rb +30 -12
  71. data/lib/grape/util/registry.rb +27 -0
  72. data/lib/grape/validations/contract_scope.rb +2 -39
  73. data/lib/grape/validations/params_scope.rb +15 -14
  74. data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
  75. data/lib/grape/validations/validator_factory.rb +2 -2
  76. data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
  77. data/lib/grape/validations/validators/base.rb +7 -11
  78. data/lib/grape/validations/validators/coerce_validator.rb +1 -1
  79. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  80. data/lib/grape/validations/validators/default_validator.rb +1 -1
  81. data/lib/grape/validations/validators/except_values_validator.rb +2 -2
  82. data/lib/grape/validations/validators/length_validator.rb +1 -1
  83. data/lib/grape/validations/validators/presence_validator.rb +1 -1
  84. data/lib/grape/validations/validators/regexp_validator.rb +2 -2
  85. data/lib/grape/validations/validators/values_validator.rb +15 -57
  86. data/lib/grape/validations.rb +8 -17
  87. data/lib/grape/version.rb +1 -1
  88. data/lib/grape.rb +14 -2
  89. metadata +24 -16
  90. data/lib/grape/http/headers.rb +0 -55
  91. data/lib/grape/middleware/helpers.rb +0 -12
  92. data/lib/grape/middleware/versioner_helpers.rb +0 -75
  93. data/lib/grape/util/lazy/object.rb +0 -45
  94. data/lib/grape/validations/types/build_coercer.rb +0 -92
data/lib/grape/api.rb CHANGED
@@ -5,7 +5,7 @@ module Grape
5
5
  # should subclass this class in order to build an API.
6
6
  class API
7
7
  # Class methods that we want to call on the API rather than on the API object
8
- NON_OVERRIDABLE = %i[call call! configuration compile! inherited].freeze
8
+ NON_OVERRIDABLE = %i[call call! configuration compile! inherited recognize_path].freeze
9
9
 
10
10
  class Boolean
11
11
  def self.build(val)
@@ -20,12 +20,18 @@ module Grape
20
20
  end
21
21
 
22
22
  class << self
23
+ extend Forwardable
23
24
  attr_accessor :base_instance, :instances
24
25
 
25
- # Rather than initializing an object of type Grape::API, create an object of type Instance
26
- def new(...)
27
- base_instance.new(...)
28
- end
26
+ delegate_missing_to :base_instance
27
+ def_delegators :base_instance, :new, :configuration
28
+
29
+ # This is the interface point between Rack and Grape; it accepts a request
30
+ # from Rack and ultimately returns an array of three values: the status,
31
+ # the headers, and the body. See [the rack specification]
32
+ # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
33
+ # NOTE: This will only be called on an API directly mounted on RACK
34
+ def_delegators :instance_for_rack, :call, :compile!
29
35
 
30
36
  # When inherited, will create a list of all instances (times the API was mounted)
31
37
  # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
@@ -40,7 +46,7 @@ module Grape
40
46
  # an instance that will be used to create the set up but will not be mounted
41
47
  def initial_setup(base_instance_parent)
42
48
  @instances = []
43
- @setup = Set.new
49
+ @setup = []
44
50
  @base_parent = base_instance_parent
45
51
  @base_instance = mount_instance
46
52
  end
@@ -49,7 +55,7 @@ module Grape
49
55
  def override_all_methods!
50
56
  (base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
51
57
  define_singleton_method(method_override) do |*args, &block|
52
- add_setup(method_override, *args, &block)
58
+ add_setup(method: method_override, args: args, block: block)
53
59
  end
54
60
  end
55
61
  end
@@ -69,67 +75,28 @@ module Grape
69
75
  end
70
76
  end
71
77
 
72
- # This is the interface point between Rack and Grape; it accepts a request
73
- # from Rack and ultimately returns an array of three values: the status,
74
- # the headers, and the body. See [the rack specification]
75
- # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
76
- # NOTE: This will only be called on an API directly mounted on RACK
77
- def call(...)
78
- instance_for_rack.call(...)
79
- end
80
-
81
- # Alleviates problems with autoloading by tring to search for the constant
82
- def const_missing(*args)
83
- if base_instance.const_defined?(*args)
84
- base_instance.const_get(*args)
85
- else
86
- super
87
- end
88
- end
89
-
90
78
  # The remountable class can have a configuration hash to provide some dynamic class-level variables.
91
79
  # For instance, a description could be done using: `desc configuration[:description]` if it may vary
92
80
  # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
93
81
  # too much, you may actually want to provide a new API rather than remount it.
94
- def mount_instance(**opts)
95
- instance = Class.new(@base_parent)
96
- instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {})
97
- instance.base = self
98
- replay_setup_on(instance)
99
- instance
82
+ def mount_instance(configuration: nil)
83
+ Class.new(@base_parent).tap do |instance|
84
+ instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
85
+ instance.base = self
86
+ replay_setup_on(instance)
87
+ end
100
88
  end
101
89
 
90
+ private
91
+
102
92
  # Replays the set up to produce an API as defined in this class, can be called
103
93
  # on classes that inherit from Grape::API
104
94
  def replay_setup_on(instance)
105
95
  @setup.each do |setup_step|
106
- replay_step_on(instance, setup_step)
107
- end
108
- end
109
-
110
- def respond_to?(method, include_private = false)
111
- super || base_instance.respond_to?(method, include_private)
112
- end
113
-
114
- def respond_to_missing?(method, include_private = false)
115
- base_instance.respond_to?(method, include_private)
116
- end
117
-
118
- def method_missing(method, *args, &block)
119
- # If there's a missing method, it may be defined on the base_instance instead.
120
- if respond_to_missing?(method)
121
- base_instance.send(method, *args, &block)
122
- else
123
- super
96
+ replay_step_on(instance, **setup_step)
124
97
  end
125
98
  end
126
99
 
127
- def compile!
128
- instance_for_rack.compile! # See API::Instance.compile!
129
- end
130
-
131
- private
132
-
133
100
  def instance_for_rack
134
101
  if never_mounted?
135
102
  base_instance
@@ -139,34 +106,35 @@ module Grape
139
106
  end
140
107
 
141
108
  # Adds a new stage to the set up require to get a Grape::API up and running
142
- def add_setup(method, *args, &block)
143
- setup_step = { method: method, args: args, block: block }
144
- @setup += [setup_step]
109
+ def add_setup(step)
110
+ @setup << step
145
111
  last_response = nil
146
112
  @instances.each do |instance|
147
- last_response = replay_step_on(instance, setup_step)
113
+ last_response = replay_step_on(instance, **step)
148
114
  end
149
115
 
150
- # Updating all previously mounted classes in the case that new methods have been executed.
151
- if method != :mount && @setup.any?
152
- previous_mount_steps = @setup.select { |step| step[:method] == :mount }
153
- previous_mount_steps.each do |mount_step|
154
- refresh_mount_step = mount_step.merge(method: :refresh_mounted_api)
155
- @setup += [refresh_mount_step]
156
- @instances.each do |instance|
157
- replay_step_on(instance, refresh_mount_step)
158
- end
116
+ refresh_mount_step if step[:method] != :mount
117
+ last_response
118
+ end
119
+
120
+ # Updating all previously mounted classes in the case that new methods have been executed.
121
+ def refresh_mount_step
122
+ @setup.each do |setup_step|
123
+ next if setup_step[:method] != :mount
124
+
125
+ refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
126
+ @setup << refresh_mount_step
127
+ @instances.each do |instance|
128
+ replay_step_on(instance, **refresh_mount_step)
159
129
  end
160
130
  end
161
-
162
- last_response
163
131
  end
164
132
 
165
- def replay_step_on(instance, setup_step)
166
- return if skip_immediate_run?(instance, setup_step[:args])
133
+ def replay_step_on(instance, method:, args:, block:)
134
+ return if skip_immediate_run?(instance, args)
167
135
 
168
- args = evaluate_arguments(instance.configuration, *setup_step[:args])
169
- response = instance.send(setup_step[:method], *args, &setup_step[:block])
136
+ eval_args = evaluate_arguments(instance.configuration, *args)
137
+ response = instance.send(method, *eval_args, &block)
170
138
  if skip_immediate_run?(instance, [response])
171
139
  response
172
140
  else
@@ -181,12 +149,12 @@ module Grape
181
149
  end
182
150
 
183
151
  def any_lazy?(args)
184
- args.any? { |argument| argument.respond_to?(:lazy?) && argument.lazy? }
152
+ args.any? { |argument| argument.try(:lazy?) }
185
153
  end
186
154
 
187
155
  def evaluate_arguments(configuration, *args)
188
156
  args.map do |argument|
189
- if argument.respond_to?(:lazy?) && argument.lazy?
157
+ if argument.try(:lazy?)
190
158
  argument.evaluate_from(configuration)
191
159
  elsif argument.is_a?(Hash)
192
160
  argument.transform_values { |value| evaluate_arguments(configuration, value).first }
data/lib/grape/cookies.rb CHANGED
@@ -2,43 +2,49 @@
2
2
 
3
3
  module Grape
4
4
  class Cookies
5
- def initialize
6
- @cookies = {}
7
- @send_cookies = {}
8
- end
5
+ extend Forwardable
9
6
 
10
- def read(request)
11
- request.cookies.each do |name, value|
12
- @cookies[name.to_s] = value
13
- end
7
+ DELETED_COOKIES_ATTRS = {
8
+ max_age: '0',
9
+ value: '',
10
+ expires: Time.at(0)
11
+ }.freeze
12
+
13
+ def_delegators :cookies, :[], :each
14
+
15
+ def initialize(rack_cookies)
16
+ @cookies = rack_cookies
17
+ @send_cookies = nil
14
18
  end
15
19
 
16
- def write(header)
17
- @cookies.select { |key, _value| @send_cookies[key] == true }.each do |name, value|
18
- cookie_value = value.is_a?(Hash) ? value : { value: value }
19
- Rack::Utils.set_cookie_header! header, name, cookie_value
20
+ def response_cookies
21
+ return unless @send_cookies
22
+
23
+ send_cookies.each do |name|
24
+ yield name, cookies[name]
20
25
  end
21
26
  end
22
27
 
23
- def [](name)
24
- @cookies[name.to_s]
28
+ def []=(name, value)
29
+ cookies[name] = value
30
+ send_cookies << name
25
31
  end
26
32
 
27
- def []=(name, value)
28
- @cookies[name.to_s] = value
29
- @send_cookies[name.to_s] = true
33
+ # see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
34
+ def delete(name, **opts)
35
+ self.[]=(name, opts.merge(DELETED_COOKIES_ATTRS))
30
36
  end
31
37
 
32
- def each(&block)
33
- @cookies.each(&block)
38
+ private
39
+
40
+ def cookies
41
+ return @cookies unless @cookies.is_a?(Proc)
42
+
43
+ @cookies = @cookies.call.with_indifferent_access
34
44
  end
35
45
 
36
- # see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
37
- # rubocop:disable Layout/SpaceBeforeBrackets
38
- def delete(name, **opts)
39
- options = opts.merge(max_age: '0', value: '', expires: Time.at(0))
40
- self.[]=(name, options)
46
+ def send_cookies
47
+ @send_cookies ||= Set.new
41
48
  end
42
- # rubocop:enable Layout/SpaceBeforeBrackets
43
49
  end
44
50
  end
data/lib/grape/dsl/api.rb CHANGED
@@ -5,8 +5,6 @@ module Grape
5
5
  module API
6
6
  extend ActiveSupport::Concern
7
7
 
8
- include Grape::Middleware::Auth::DSL
9
-
10
8
  include Grape::DSL::Validations
11
9
  include Grape::DSL::Callbacks
12
10
  include Grape::DSL::Configuration
@@ -70,33 +70,23 @@ module Grape
70
70
  # # ...
71
71
  # end
72
72
  #
73
- def desc(description, options = {}, &config_block)
74
- if config_block
75
- endpoint_configuration = if defined?(configuration)
76
- # When the instance is mounted - the configuration is executed on mount time
77
- if configuration.respond_to?(:evaluate)
78
- configuration.evaluate
79
- # Within `given` or `mounted blocks` the configuration is already evaluated
80
- elsif configuration.is_a?(Hash)
81
- configuration
82
- end
83
- end
84
- endpoint_configuration ||= {}
85
- config_class = desc_container(endpoint_configuration)
73
+ def desc(description, options = nil, &config_block)
74
+ opts =
75
+ if config_block
76
+ desc_container(endpoint_configuration).then do |config_class|
77
+ config_class.configure do
78
+ description(description)
79
+ end
86
80
 
87
- config_class.configure do
88
- description description
81
+ config_class.configure(&config_block)
82
+ config_class.settings
83
+ end
84
+ else
85
+ options&.merge(description: description) || { description: description }
89
86
  end
90
87
 
91
- config_class.configure(&config_block)
92
- Grape.deprecator.warn('Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.') if options.any?
93
- options = config_class.settings
94
- else
95
- options = options.merge(description: description)
96
- end
97
-
98
- namespace_setting :description, options
99
- route_setting :description, options
88
+ namespace_setting :description, opts
89
+ route_setting :description, opts
100
90
  end
101
91
 
102
92
  # Returns an object which configures itself via an instance-context DSL.
@@ -116,6 +106,19 @@ module Grape
116
106
  end
117
107
  end
118
108
  end
109
+
110
+ private
111
+
112
+ def endpoint_configuration
113
+ return {} unless defined?(configuration)
114
+
115
+ if configuration.respond_to?(:evaluate)
116
+ configuration.evaluate
117
+ # Within `given` or `mounted blocks` the configuration is already evaluated
118
+ elsif configuration.is_a?(Hash)
119
+ configuration
120
+ end
121
+ end
119
122
  end
120
123
  end
121
124
  end
@@ -10,7 +10,7 @@ module Grape
10
10
  # 4. Delete a specifc header key-value pair
11
11
  def header(key = nil, val = nil)
12
12
  if key
13
- val ? header[key.to_s] = val : header.delete(key.to_s)
13
+ val ? header[key] = val : header.delete(key)
14
14
  else
15
15
  @header ||= Grape::Util::Header.new
16
16
  end
@@ -98,7 +98,7 @@ module Grape
98
98
  protected
99
99
 
100
100
  def process_named_params
101
- return unless instance_variable_defined?(:@named_params) && @named_params && @named_params.any?
101
+ return if @named_params.blank?
102
102
 
103
103
  api.namespace_stackable(:named_params, @named_params)
104
104
  end
@@ -26,8 +26,8 @@ module Grape
26
26
  # has completed
27
27
  module PostBeforeFilter
28
28
  def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
29
- options = options.reverse_merge(include_missing: true, include_parent_namespaces: true, evaluate_given: false)
30
- declared_params ||= optioned_declared_params(**options)
29
+ options.reverse_merge!(include_missing: true, include_parent_namespaces: true, evaluate_given: false)
30
+ declared_params ||= optioned_declared_params(options[:include_parent_namespaces])
31
31
 
32
32
  res = if passed_params.is_a?(Array)
33
33
  declared_array(passed_params, options, declared_params, params_nested_path)
@@ -79,7 +79,7 @@ module Grape
79
79
  else
80
80
  # If it is not a Hash then it does not have children.
81
81
  # Find its value or set it to nil.
82
- return unless options[:include_missing] || passed_params.key?(declared_param)
82
+ return unless options[:include_missing] || passed_params.try(:key?, declared_param)
83
83
 
84
84
  rename_path = params_nested_path + [declared_param.to_s]
85
85
  renamed_param_name = renamed_params[rename_path]
@@ -107,7 +107,7 @@ module Grape
107
107
 
108
108
  if type == 'Hash' && !has_children
109
109
  {}
110
- elsif type == 'Array' || (type&.start_with?('[') && type&.exclude?(','))
110
+ elsif type == 'Array' || (type&.start_with?('[') && type.exclude?(','))
111
111
  []
112
112
  elsif type == 'Set' || type&.start_with?('#<Set')
113
113
  Set.new
@@ -120,8 +120,8 @@ module Grape
120
120
  options[:stringify] ? declared_param.to_s : declared_param.to_sym
121
121
  end
122
122
 
123
- def optioned_declared_params(**options)
124
- declared_params = if options[:include_parent_namespaces]
123
+ def optioned_declared_params(include_parent_namespaces)
124
+ declared_params = if include_parent_namespaces
125
125
  # Declared params including parent namespaces
126
126
  route_setting(:declared_params)
127
127
  else
@@ -199,10 +199,9 @@ module Grape
199
199
  # Redirect to a new url.
200
200
  #
201
201
  # @param url [String] The url to be redirect.
202
- # @param options [Hash] The options used when redirect.
203
- # :permanent, default false.
204
- # :body, default a short message including the URL.
205
- def redirect(url, permanent: false, body: nil, **_options)
202
+ # @param permanent [Boolean] default false.
203
+ # @param body default a short message including the URL.
204
+ def redirect(url, permanent: false, body: nil)
206
205
  body_message = body
207
206
  if permanent
208
207
  status 301
@@ -214,7 +213,7 @@ module Grape
214
213
  status 302
215
214
  body_message ||= "This resource has been moved temporarily to #{url}."
216
215
  end
217
- header Grape::Http::Headers::LOCATION, url
216
+ header 'Location', url
218
217
  content_type 'text/plain'
219
218
  body body_message
220
219
  end
@@ -258,18 +257,6 @@ module Grape
258
257
  end
259
258
  end
260
259
 
261
- # Set or get a cookie
262
- #
263
- # @example
264
- # cookies[:mycookie] = 'mycookie val'
265
- # cookies['mycookie-string'] = 'mycookie string val'
266
- # cookies[:more] = { value: '123', expires: Time.at(0) }
267
- # cookies.delete :more
268
- #
269
- def cookies
270
- @cookies ||= Cookies.new
271
- end
272
-
273
260
  # Allows you to define the response body as something other than the
274
261
  # return value.
275
262
  #
@@ -305,20 +292,6 @@ module Grape
305
292
  body false
306
293
  end
307
294
 
308
- # Deprecated method to send files to the client. Use `sendfile` or `stream`
309
- def file(value = nil)
310
- if value.is_a?(String)
311
- Grape.deprecator.warn('Use sendfile or stream to send files.')
312
- sendfile(value)
313
- elsif !value.is_a?(NilClass)
314
- Grape.deprecator.warn('Use stream to use a Stream object.')
315
- stream(value)
316
- else
317
- Grape.deprecator.warn('Use sendfile or stream to send files.')
318
- sendfile
319
- end
320
- end
321
-
322
295
  # Allows you to send a file to the client via sendfile.
323
296
  #
324
297
  # @example
@@ -357,7 +330,7 @@ module Grape
357
330
  return if value.nil? && @stream.nil?
358
331
 
359
332
  header Rack::CONTENT_LENGTH, nil
360
- header Grape::Http::Headers::TRANSFER_ENCODING, nil
333
+ header 'Transfer-Encoding', nil
361
334
  header Rack::CACHE_CONTROL, 'no-cache' # Skips ETag generation (reading the response up front)
362
335
  if value.is_a?(String)
363
336
  file_body = Grape::ServeStream::FileBody.new(value)
@@ -462,11 +435,15 @@ module Grape
462
435
  def entity_representation_for(entity_class, object, options)
463
436
  embeds = { env: env }
464
437
  embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
465
- entity_class.represent(object, **embeds.merge(options))
438
+ entity_class.represent(object, **embeds, **options)
466
439
  end
467
440
 
468
441
  def http_version
469
- env['HTTP_VERSION'] || env[Rack::SERVER_PROTOCOL]
442
+ env.fetch('HTTP_VERSION') { env[Rack::SERVER_PROTOCOL] }
443
+ end
444
+
445
+ def api_format(format)
446
+ env[Grape::Env::API_FORMAT] = format
470
447
  end
471
448
 
472
449
  def context
@@ -23,14 +23,14 @@ module Grape
23
23
  # class API < Grape::API
24
24
  # desc "Get collection"
25
25
  # params do
26
- # build_with Grape::Extensions::Hashie::Mash::ParamBuilder
26
+ # build_with :hashie_mash
27
27
  # requires :user_id, type: Integer
28
28
  # end
29
29
  # get do
30
30
  # params['user_id']
31
31
  # end
32
32
  # end
33
- def build_with(build_with = nil)
33
+ def build_with(build_with)
34
34
  @api.namespace_inheritable(:build_params_with, build_with)
35
35
  end
36
36
 
@@ -136,7 +136,7 @@ module Grape
136
136
  require_required_and_optional_fields(attrs.first, opts)
137
137
  else
138
138
  validate_attributes(attrs, opts, &block)
139
- block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
139
+ block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, opts.slice(:as))
140
140
  end
141
141
  end
142
142
 
@@ -162,7 +162,7 @@ module Grape
162
162
  else
163
163
  validate_attributes(attrs, opts, &block)
164
164
 
165
- block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
165
+ block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, opts.slice(:as))
166
166
  end
167
167
  end
168
168
 
@@ -251,7 +251,7 @@ module Grape
251
251
  # @return hash of parameters relevant for the current scope
252
252
  # @api private
253
253
  def params(params)
254
- params = @parent.params(params) if instance_variable_defined?(:@parent) && @parent
254
+ params = @parent.params_meeting_dependency.presence || @parent.params(params) if instance_variable_defined?(:@parent) && @parent
255
255
  params = map_params(params, @element) if instance_variable_defined?(:@element) && @element
256
256
  params
257
257
  end
@@ -67,6 +67,10 @@ module Grape
67
67
  end
68
68
  end
69
69
 
70
+ def build_with(build_with)
71
+ namespace_inheritable(:build_params_with, build_with)
72
+ end
73
+
70
74
  # Do not route HEAD requests to GET requests automatically.
71
75
  def do_not_route_head!
72
76
  namespace_inheritable(:do_not_route_head, true)
@@ -77,6 +81,10 @@ module Grape
77
81
  namespace_inheritable(:do_not_route_options, true)
78
82
  end
79
83
 
84
+ def lint!
85
+ namespace_inheritable(:lint, true)
86
+ end
87
+
80
88
  def do_not_document!
81
89
  namespace_inheritable(:do_not_document, true)
82
90
  end
@@ -154,7 +162,7 @@ module Grape
154
162
  reset_validations!
155
163
  end
156
164
 
157
- Grape::Http::Headers::SUPPORTED_METHODS.each do |supported_method|
165
+ Grape::HTTP_SUPPORTED_METHODS.each do |supported_method|
158
166
  define_method supported_method.downcase do |*args, &block|
159
167
  options = args.extract_options!
160
168
  paths = args.first || ['/']
@@ -175,19 +183,12 @@ module Grape
175
183
  # end
176
184
  # end
177
185
  def namespace(space = nil, options = {}, &block)
178
- @namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
179
-
180
- if space || block
181
- within_namespace do
182
- previous_namespace_description = @namespace_description
183
- @namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
184
- nest(block) do
185
- namespace_stackable(:namespace, Namespace.new(space, **options)) if space
186
- end
187
- @namespace_description = previous_namespace_description
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
188
191
  end
189
- else
190
- Namespace.joined_space_path(namespace_stackable(:namespace))
191
192
  end
192
193
  end
193
194