grape 0.12.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +9 -4
- data/CHANGELOG.md +265 -215
- data/CONTRIBUTING.md +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +426 -161
- data/RELEASING.md +14 -6
- data/Rakefile +30 -33
- data/UPGRADING.md +54 -23
- data/benchmark/simple.rb +27 -0
- data/gemfiles/rack_1.5.2.gemfile +13 -0
- data/gemfiles/rails_3.gemfile +2 -2
- data/gemfiles/rails_4.gemfile +1 -2
- data/grape.gemspec +6 -7
- data/lib/grape/api.rb +24 -4
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/configuration.rb +59 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +100 -45
- data/lib/grape/dsl/parameters.rb +96 -7
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +17 -4
- data/lib/grape/dsl/settings.rb +36 -1
- data/lib/grape/dsl/validations.rb +7 -5
- data/lib/grape/endpoint.rb +102 -57
- data/lib/grape/error_formatter/base.rb +6 -6
- data/lib/grape/exceptions/base.rb +5 -5
- data/lib/grape/exceptions/invalid_version_header.rb +10 -0
- data/lib/grape/exceptions/unknown_parameter.rb +10 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -3
- data/lib/grape/formatter/serializable_hash.rb +3 -2
- data/lib/grape/http/headers.rb +0 -1
- data/lib/grape/locale/en.yml +5 -1
- data/lib/grape/middleware/auth/base.rb +2 -2
- data/lib/grape/middleware/auth/dsl.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/base.rb +8 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +64 -45
- data/lib/grape/middleware/globals.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
- data/lib/grape/middleware/versioner/header.rb +113 -50
- data/lib/grape/middleware/versioner/param.rb +5 -8
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
- data/lib/grape/middleware/versioner/path.rb +3 -6
- data/lib/grape/namespace.rb +13 -2
- data/lib/grape/path.rb +4 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/route.rb +5 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- data/lib/grape/util/file_response.rb +21 -0
- data/lib/grape/util/inheritable_setting.rb +23 -2
- data/lib/grape/util/inheritable_values.rb +1 -1
- data/lib/grape/util/stackable_values.rb +5 -2
- data/lib/grape/util/strict_hash_configuration.rb +2 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -3
- data/lib/grape/validations/params_scope.rb +164 -22
- data/lib/grape/validations/types/build_coercer.rb +53 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
- data/lib/grape/validations/types/file.rb +28 -0
- data/lib/grape/validations/types/json.rb +65 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
- data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
- data/lib/grape/validations/types.rb +144 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -3
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/coerce.rb +32 -34
- data/lib/grape/validations/validators/presence.rb +2 -3
- data/lib/grape/validations/validators/regexp.rb +2 -4
- data/lib/grape/validations/validators/values.rb +3 -3
- data/lib/grape/validations.rb +5 -0
- data/lib/grape/version.rb +2 -1
- data/lib/grape.rb +15 -12
- data/pkg/grape-0.13.0.gem +0 -0
- data/spec/grape/api/custom_validations_spec.rb +5 -4
- data/spec/grape/api/deeply_included_options_spec.rb +7 -7
- data/spec/grape/api/nested_helpers_spec.rb +4 -2
- data/spec/grape/api/shared_helpers_spec.rb +8 -8
- data/spec/grape/api_spec.rb +151 -54
- data/spec/grape/dsl/configuration_spec.rb +13 -0
- data/spec/grape/dsl/helpers_spec.rb +16 -2
- data/spec/grape/dsl/inside_route_spec.rb +40 -4
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/dsl/validations_spec.rb +18 -0
- data/spec/grape/endpoint_spec.rb +130 -6
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/exceptions/validation_errors_spec.rb +28 -0
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +40 -16
- data/spec/grape/middleware/error_spec.rb +16 -15
- data/spec/grape/middleware/exception_spec.rb +45 -43
- data/spec/grape/middleware/formatter_spec.rb +34 -5
- data/spec/grape/middleware/versioner/header_spec.rb +79 -47
- data/spec/grape/path_spec.rb +10 -10
- data/spec/grape/presenters/presenter_spec.rb +2 -2
- data/spec/grape/request_spec.rb +100 -0
- data/spec/grape/util/inheritable_values_spec.rb +14 -0
- data/spec/grape/util/stackable_values_spec.rb +10 -0
- data/spec/grape/validations/params_scope_spec.rb +86 -0
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +364 -10
- data/spec/grape/validations/validators/values_spec.rb +27 -15
- data/spec/grape/validations_spec.rb +53 -24
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/versioned_helpers.rb +2 -2
- metadata +55 -14
- data/.gitignore +0 -46
- data/.rspec +0 -2
- data/.rubocop.yml +0 -7
- data/.rubocop_todo.yml +0 -84
- data/.travis.yml +0 -20
- data/.yardopts +0 -2
- data/lib/backports/active_support/deep_dup.rb +0 -49
- data/lib/backports/active_support/duplicable.rb +0 -88
- data/lib/grape/http/request.rb +0 -27
data/lib/grape/dsl/settings.rb
CHANGED
@@ -2,24 +2,37 @@ require 'active_support/concern'
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module DSL
|
5
|
+
# Keeps track of settings (impemented as key-value pairs, grouped by
|
6
|
+
# types), in two contexts: top-level settings which apply globally no
|
7
|
+
# matter where they're defined, and inheritable settings which apply only
|
8
|
+
# in the current scope and scopes nested under it.
|
5
9
|
module Settings
|
6
10
|
extend ActiveSupport::Concern
|
7
11
|
|
8
12
|
attr_accessor :inheritable_setting, :top_level_setting
|
9
13
|
|
14
|
+
# Fetch our top-level settings, which apply to all endpoints in the API.
|
10
15
|
def top_level_setting
|
11
16
|
@top_level_setting ||= Grape::Util::InheritableSetting.new
|
12
17
|
end
|
13
18
|
|
19
|
+
# Fetch our current inheritable settings, which are inherited by
|
20
|
+
# nested scopes but not shared across siblings.
|
14
21
|
def inheritable_setting
|
15
22
|
@inheritable_setting ||= Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from top_level_setting }
|
16
23
|
end
|
17
24
|
|
25
|
+
# @param type [Symbol]
|
26
|
+
# @param key [Symbol]
|
18
27
|
def unset(type, key)
|
19
28
|
setting = inheritable_setting.send(type)
|
20
29
|
setting.delete key
|
21
30
|
end
|
22
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
|
23
36
|
def get_or_set(type, key, value)
|
24
37
|
setting = inheritable_setting.send(type)
|
25
38
|
if value.nil?
|
@@ -29,72 +42,94 @@ module Grape
|
|
29
42
|
end
|
30
43
|
end
|
31
44
|
|
45
|
+
# @param key [Symbol]
|
46
|
+
# @param value [Object]
|
47
|
+
# @return (see #get_or_set)
|
32
48
|
def global_setting(key, value = nil)
|
33
49
|
get_or_set :global, key, value
|
34
50
|
end
|
35
51
|
|
52
|
+
# @param key [Symbol]
|
36
53
|
def unset_global_setting(key)
|
37
54
|
unset :global, key
|
38
55
|
end
|
39
56
|
|
57
|
+
# (see #global_setting)
|
40
58
|
def route_setting(key, value = nil)
|
41
59
|
get_or_set :route, key, value
|
42
60
|
end
|
43
61
|
|
62
|
+
# (see #unset_global_setting)
|
44
63
|
def unset_route_setting(key)
|
45
64
|
unset :route, key
|
46
65
|
end
|
47
66
|
|
67
|
+
# (see #global_setting)
|
48
68
|
def namespace_setting(key, value = nil)
|
49
69
|
get_or_set :namespace, key, value
|
50
70
|
end
|
51
71
|
|
72
|
+
# (see #unset_global_setting)
|
52
73
|
def unset_namespace_setting(key)
|
53
74
|
unset :namespace_setting, key
|
54
75
|
end
|
55
76
|
|
77
|
+
# (see #global_setting)
|
56
78
|
def namespace_inheritable(key, value = nil)
|
57
79
|
get_or_set :namespace_inheritable, key, value
|
58
80
|
end
|
59
81
|
|
82
|
+
# (see #unset_global_setting)
|
60
83
|
def unset_namespace_inheritable(key)
|
61
84
|
unset :namespace_inheritable, key
|
62
85
|
end
|
63
86
|
|
87
|
+
# @param key [Symbol]
|
64
88
|
def namespace_inheritable_to_nil(key)
|
65
89
|
inheritable_setting.namespace_inheritable[key] = nil
|
66
90
|
end
|
67
91
|
|
92
|
+
# (see #global_setting)
|
68
93
|
def namespace_stackable(key, value = nil)
|
69
94
|
get_or_set :namespace_stackable, key, value
|
70
95
|
end
|
71
96
|
|
97
|
+
# (see #unset_global_setting)
|
72
98
|
def unset_namespace_stackable(key)
|
73
99
|
unset :namespace_stackable, key
|
74
100
|
end
|
75
101
|
|
102
|
+
# (see #global_setting)
|
76
103
|
def api_class_setting(key, value = nil)
|
77
104
|
get_or_set :api_class, key, value
|
78
105
|
end
|
79
106
|
|
107
|
+
# (see #unset_global_setting)
|
80
108
|
def unset_api_class_setting(key)
|
81
109
|
unset :api_class_setting, key
|
82
110
|
end
|
83
111
|
|
112
|
+
# Fork our inheritable settings to a new instance, copied from our
|
113
|
+
# parent's, but separate so we won't modify it. Every call to this
|
114
|
+
# method should have an answering call to #namespace_end.
|
84
115
|
def namespace_start
|
85
116
|
@inheritable_setting = Grape::Util::InheritableSetting.new.tap { |new_settings| new_settings.inherit_from inheritable_setting }
|
86
117
|
end
|
87
118
|
|
119
|
+
# Set the inheritable settings pointer back up by one level.
|
88
120
|
def namespace_end
|
89
121
|
route_end
|
90
122
|
@inheritable_setting = inheritable_setting.parent
|
91
123
|
end
|
92
124
|
|
125
|
+
# Stop defining settings for the current route and clear them for the
|
126
|
+
# next, within a namespace.
|
93
127
|
def route_end
|
94
128
|
inheritable_setting.route_end
|
95
|
-
reset_validations!
|
96
129
|
end
|
97
130
|
|
131
|
+
# Execute the block within a context where our inheritable settings are forked
|
132
|
+
# to a new copy (see #namespace_start).
|
98
133
|
def within_namespace(&_block)
|
99
134
|
namespace_start
|
100
135
|
|
@@ -8,22 +8,24 @@ module Grape
|
|
8
8
|
include Grape::DSL::Configuration
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
+
# Clears all defined parameters and validations.
|
11
12
|
def reset_validations!
|
12
13
|
unset_namespace_stackable :declared_params
|
13
14
|
unset_namespace_stackable :validations
|
14
15
|
unset_namespace_stackable :params
|
16
|
+
unset_description_field :params
|
15
17
|
end
|
16
18
|
|
19
|
+
# Opens a root-level ParamsScope, defining parameter coercions and
|
20
|
+
# validations for the endpoint.
|
21
|
+
# @yield instance context of the new scope
|
17
22
|
def params(&block)
|
18
23
|
Grape::Validations::ParamsScope.new(api: self, type: Hash, &block)
|
19
24
|
end
|
20
25
|
|
21
26
|
def document_attribute(names, opts)
|
22
|
-
|
23
|
-
|
24
|
-
route_setting(:description)[:params] ||= {}
|
25
|
-
|
26
|
-
setting = route_setting(:description)[:params]
|
27
|
+
setting = description_field(:params)
|
28
|
+
setting ||= description_field(:params, {})
|
27
29
|
Array(names).each do |name|
|
28
30
|
setting[name[:full_name].to_s] ||= {}
|
29
31
|
setting[name[:full_name].to_s].merge!(opts)
|
data/lib/grape/endpoint.rb
CHANGED
@@ -12,15 +12,31 @@ module Grape
|
|
12
12
|
include Grape::DSL::InsideRoute
|
13
13
|
|
14
14
|
class << self
|
15
|
+
def new(*args, &block)
|
16
|
+
if self == Endpoint
|
17
|
+
Class.new(Endpoint).new(*args, &block)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
def before_each(new_setup = false, &block)
|
24
|
+
@before_each ||= []
|
16
25
|
if new_setup == false
|
17
26
|
if block_given?
|
18
|
-
@before_each
|
27
|
+
@before_each << block
|
19
28
|
else
|
20
29
|
return @before_each
|
21
30
|
end
|
22
31
|
else
|
23
|
-
@before_each = new_setup
|
32
|
+
@before_each = [new_setup]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_before_each(endpoint)
|
37
|
+
superclass.run_before_each(endpoint) unless self == Endpoint
|
38
|
+
before_each.each do |blk|
|
39
|
+
blk.call(endpoint) if blk.respond_to? :call
|
24
40
|
end
|
25
41
|
end
|
26
42
|
|
@@ -41,10 +57,16 @@ module Grape
|
|
41
57
|
if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s)
|
42
58
|
fail NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
|
43
59
|
end
|
60
|
+
|
44
61
|
define_method(method_name, &block)
|
45
62
|
method = instance_method(method_name)
|
46
63
|
remove_method(method_name)
|
47
|
-
|
64
|
+
|
65
|
+
proc do |endpoint_instance|
|
66
|
+
ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
|
67
|
+
method.bind(endpoint_instance).call
|
68
|
+
end
|
69
|
+
end
|
48
70
|
end
|
49
71
|
end
|
50
72
|
|
@@ -68,10 +90,12 @@ module Grape
|
|
68
90
|
@options[:method] = Array(options[:method])
|
69
91
|
@options[:route_options] ||= {}
|
70
92
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
93
|
+
@lazy_initialize_lock = Mutex.new
|
94
|
+
|
95
|
+
return unless block_given?
|
96
|
+
|
97
|
+
@source = block
|
98
|
+
@block = self.class.generate_api_method(method_name, &block)
|
75
99
|
end
|
76
100
|
|
77
101
|
def require_option(options, key)
|
@@ -113,7 +137,7 @@ module Grape
|
|
113
137
|
route_set.add_route(self, {
|
114
138
|
path_info: route.route_compiled,
|
115
139
|
request_method: method
|
116
|
-
},
|
140
|
+
}, route_info: route)
|
117
141
|
end
|
118
142
|
end
|
119
143
|
end
|
@@ -181,20 +205,14 @@ module Grape
|
|
181
205
|
end
|
182
206
|
|
183
207
|
def call(env)
|
208
|
+
lazy_initialize!
|
184
209
|
dup.call!(env)
|
185
210
|
end
|
186
211
|
|
187
212
|
def call!(env)
|
188
|
-
|
189
|
-
|
190
|
-
env
|
191
|
-
if options[:app]
|
192
|
-
options[:app].call(env)
|
193
|
-
else
|
194
|
-
builder = build_middleware
|
195
|
-
builder.run ->(arg) { run(arg) }
|
196
|
-
builder.call(env)
|
197
|
-
end
|
213
|
+
env[Grape::Env::API_ENDPOINT] = self
|
214
|
+
@env = env
|
215
|
+
@app.call(env)
|
198
216
|
end
|
199
217
|
|
200
218
|
# Return the collection of endpoints within this endpoint.
|
@@ -209,51 +227,50 @@ module Grape
|
|
209
227
|
|
210
228
|
protected
|
211
229
|
|
212
|
-
def run
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
@request = Grape::Request.new(env)
|
217
|
-
@params = @request.params
|
218
|
-
@headers = @request.headers
|
230
|
+
def run
|
231
|
+
ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
|
232
|
+
@header = {}
|
219
233
|
|
220
|
-
|
234
|
+
@request = Grape::Request.new(env)
|
235
|
+
@params = @request.params
|
236
|
+
@headers = @request.headers
|
221
237
|
|
222
|
-
|
238
|
+
cookies.read(@request)
|
223
239
|
|
224
|
-
|
240
|
+
self.class.run_before_each(self)
|
225
241
|
|
226
|
-
|
242
|
+
run_filters befores, :before
|
227
243
|
|
228
|
-
|
229
|
-
validation_errors = []
|
244
|
+
run_filters before_validations, :before_validation
|
230
245
|
|
231
|
-
|
246
|
+
# Retrieve validations from this namespace and all parent namespaces.
|
247
|
+
validation_errors = []
|
232
248
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
249
|
+
route_setting(:saved_validations).each do |validator|
|
250
|
+
begin
|
251
|
+
validator.validate!(params)
|
252
|
+
rescue Grape::Exceptions::Validation => e
|
253
|
+
validation_errors << e
|
254
|
+
end
|
238
255
|
end
|
239
|
-
end
|
240
256
|
|
241
|
-
|
242
|
-
|
243
|
-
|
257
|
+
if validation_errors.any?
|
258
|
+
fail Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header
|
259
|
+
end
|
244
260
|
|
245
|
-
|
261
|
+
run_filters after_validations, :after_validation
|
246
262
|
|
247
|
-
|
248
|
-
|
249
|
-
|
263
|
+
response_object = @block ? @block.call(self) : nil
|
264
|
+
run_filters afters, :after
|
265
|
+
cookies.write(header)
|
250
266
|
|
251
|
-
|
252
|
-
|
253
|
-
|
267
|
+
# The Body commonly is an Array of Strings, the application instance itself, or a File-like object.
|
268
|
+
response_object = file || [body || response_object]
|
269
|
+
[status, header, response_object]
|
270
|
+
end
|
254
271
|
end
|
255
272
|
|
256
|
-
def
|
273
|
+
def build_stack
|
257
274
|
b = Rack::Builder.new
|
258
275
|
|
259
276
|
b.use Rack::Head
|
@@ -294,21 +311,49 @@ module Grape
|
|
294
311
|
formatters: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:formatters)),
|
295
312
|
parsers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:parsers))
|
296
313
|
|
297
|
-
b
|
314
|
+
b.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
315
|
+
|
316
|
+
b.to_app
|
298
317
|
end
|
299
318
|
|
319
|
+
def build_helpers
|
320
|
+
helpers = namespace_stackable(:helpers) || []
|
321
|
+
Module.new do
|
322
|
+
helpers.each do |mod_to_include|
|
323
|
+
include mod_to_include
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
private :build_stack, :build_helpers
|
329
|
+
|
300
330
|
def helpers
|
301
|
-
|
302
|
-
|
303
|
-
|
331
|
+
lazy_initialize! && @helpers
|
332
|
+
end
|
333
|
+
|
334
|
+
def lazy_initialize!
|
335
|
+
return true if @lazy_initialized
|
336
|
+
|
337
|
+
@lazy_initialize_lock.synchronize do
|
338
|
+
return true if @lazy_initialized
|
339
|
+
|
340
|
+
@app = options[:app] || build_stack
|
341
|
+
@helpers = build_helpers.tap do |mod|
|
342
|
+
self.class.send(:include, mod)
|
343
|
+
end
|
344
|
+
|
345
|
+
@lazy_initialized = true
|
304
346
|
end
|
305
|
-
mod
|
306
347
|
end
|
307
348
|
|
308
|
-
def run_filters(filters)
|
309
|
-
(filters
|
310
|
-
|
349
|
+
def run_filters(filters, type = :other)
|
350
|
+
ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do
|
351
|
+
(filters || []).each do |filter|
|
352
|
+
instance_eval(&filter)
|
353
|
+
end
|
311
354
|
end
|
355
|
+
post_extension = DSL::InsideRoute.post_filter_methods(type)
|
356
|
+
extend post_extension if post_extension
|
312
357
|
end
|
313
358
|
|
314
359
|
def befores
|
@@ -33,22 +33,22 @@ module Grape
|
|
33
33
|
present_options = {}
|
34
34
|
present_options[:with] = message.delete(:with) if message.is_a?(Hash)
|
35
35
|
|
36
|
-
presenter = env[
|
36
|
+
presenter = env[Grape::Env::API_ENDPOINT].entity_class_for_obj(message, present_options)
|
37
37
|
|
38
|
-
unless presenter || env[
|
38
|
+
unless presenter || env[Grape::Env::RACK_ROUTING_ARGS].nil?
|
39
39
|
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
40
40
|
# the Endpoint does not have a valid env at this moment
|
41
|
-
http_codes = env[
|
41
|
+
http_codes = env[Grape::Env::RACK_ROUTING_ARGS][:route_info].route_http_codes || []
|
42
42
|
found_code = http_codes.find do |http_code|
|
43
|
-
(http_code[0].to_i == env[
|
44
|
-
end if env[
|
43
|
+
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
44
|
+
end if env[Grape::Env::API_ENDPOINT].request
|
45
45
|
|
46
46
|
presenter = found_code[2] if found_code
|
47
47
|
end
|
48
48
|
|
49
49
|
if presenter
|
50
50
|
embeds = { env: env }
|
51
|
-
embeds[:version] = env[
|
51
|
+
embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
|
52
52
|
message = presenter.represent(message, embeds).serializable_hash
|
53
53
|
end
|
54
54
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Grape
|
2
2
|
module Exceptions
|
3
3
|
class Base < StandardError
|
4
|
-
BASE_MESSAGES_KEY = 'grape.errors.messages'
|
5
|
-
BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
|
4
|
+
BASE_MESSAGES_KEY = 'grape.errors.messages'.freeze
|
5
|
+
BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'.freeze
|
6
6
|
FALLBACK_LOCALE = :en
|
7
7
|
|
8
8
|
attr_reader :status, :message, :headers
|
@@ -51,16 +51,16 @@ module Grape
|
|
51
51
|
|
52
52
|
def translate_attributes(keys, options = {})
|
53
53
|
keys.map do |key|
|
54
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}",
|
54
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", options.reverse_merge(default: key))
|
55
55
|
end.join(', ')
|
56
56
|
end
|
57
57
|
|
58
58
|
def translate_attribute(key, options = {})
|
59
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}",
|
59
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", options.reverse_merge(default: key))
|
60
60
|
end
|
61
61
|
|
62
62
|
def translate_message(key, options = {})
|
63
|
-
translate("#{BASE_MESSAGES_KEY}.#{key}",
|
63
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", options.reverse_merge(default: ''))
|
64
64
|
end
|
65
65
|
|
66
66
|
def translate(key, options = {})
|
@@ -13,7 +13,8 @@ module Grape
|
|
13
13
|
@errors[validation_error.params] ||= []
|
14
14
|
@errors[validation_error.params] << validation_error
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
|
+
super message: full_messages.join(', '), status: 400, headers: args[:headers]
|
17
18
|
end
|
18
19
|
|
19
20
|
def each
|
@@ -37,12 +38,12 @@ module Grape
|
|
37
38
|
as_json.to_json
|
38
39
|
end
|
39
40
|
|
40
|
-
private
|
41
|
-
|
42
41
|
def full_messages
|
43
42
|
map { |attributes, error| full_message(attributes, error) }.uniq
|
44
43
|
end
|
45
44
|
|
45
|
+
private
|
46
|
+
|
46
47
|
def full_message(attributes, error)
|
47
48
|
I18n.t(
|
48
49
|
'grape.errors.format'.to_sym,
|
@@ -21,10 +21,11 @@ module Grape
|
|
21
21
|
elsif object.is_a?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
|
22
22
|
object.map(&:serializable_hash)
|
23
23
|
elsif object.is_a?(Hash)
|
24
|
-
|
24
|
+
h = {}
|
25
|
+
object.each_pair do |k, v|
|
25
26
|
h[k] = serialize(v)
|
26
|
-
h
|
27
27
|
end
|
28
|
+
h
|
28
29
|
else
|
29
30
|
object
|
30
31
|
end
|
data/lib/grape/http/headers.rb
CHANGED
data/lib/grape/locale/en.yml
CHANGED
@@ -28,13 +28,14 @@ en:
|
|
28
28
|
resolution: 'available strategy for :using is :path, :header, :param'
|
29
29
|
unknown_validator: 'unknown validator: %{validator_type}'
|
30
30
|
unknown_options: 'unknown options: %{options}'
|
31
|
+
unknown_parameter: 'unknown parameter: %{param}'
|
31
32
|
incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
|
32
33
|
mutual_exclusion: 'are mutually exclusive'
|
33
34
|
at_least_one: 'are missing, at least one parameter must be provided'
|
34
35
|
exactly_one: 'are missing, exactly one parameter must be provided'
|
35
36
|
all_or_none: 'provide all or none of parameters'
|
36
37
|
missing_group_type: 'group type is required'
|
37
|
-
unsupported_group_type: 'group type must be Array or
|
38
|
+
unsupported_group_type: 'group type must be Array, Hash, JSON or Array[JSON]'
|
38
39
|
invalid_message_body:
|
39
40
|
problem: "message body does not match declared format"
|
40
41
|
resolution:
|
@@ -44,4 +45,7 @@ en:
|
|
44
45
|
invalid_accept_header:
|
45
46
|
problem: 'Invalid accept header'
|
46
47
|
resolution: '%{message}'
|
48
|
+
invalid_version_header:
|
49
|
+
problem: 'Invalid version header'
|
50
|
+
resolution: '%{message}'
|
47
51
|
|
@@ -12,7 +12,7 @@ module Grape
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def context
|
15
|
-
env[
|
15
|
+
env[Grape::Env::API_ENDPOINT]
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(env)
|
@@ -26,7 +26,7 @@ module Grape
|
|
26
26
|
auth_proc = options[:proc]
|
27
27
|
auth_proc_context = context
|
28
28
|
|
29
|
-
strategy_info
|
29
|
+
strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
|
30
30
|
|
31
31
|
throw(:error, status: 401, message: 'API Authorization Failed.') unless strategy_info.present?
|
32
32
|
|
@@ -12,7 +12,7 @@ module Grape
|
|
12
12
|
# only `:http_basic`, `:http_digest` are supported.
|
13
13
|
def auth(type = nil, options = {}, &block)
|
14
14
|
if type
|
15
|
-
namespace_inheritable(:auth,
|
15
|
+
namespace_inheritable(:auth, options.reverse_merge(type: type.to_sym, proc: block))
|
16
16
|
use Grape::Middleware::Auth::Base, namespace_inheritable(:auth)
|
17
17
|
else
|
18
18
|
namespace_inheritable(:auth)
|
@@ -2,6 +2,7 @@ module Grape
|
|
2
2
|
module Middleware
|
3
3
|
class Base
|
4
4
|
attr_reader :app, :env, :options
|
5
|
+
TEXT_HTML = 'text/html'.freeze
|
5
6
|
|
6
7
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
7
8
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
@@ -37,6 +38,7 @@ module Grape
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def response
|
41
|
+
return @app_response if @app_response.is_a?(Rack::Response)
|
40
42
|
Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
41
43
|
end
|
42
44
|
|
@@ -49,13 +51,15 @@ module Grape
|
|
49
51
|
end
|
50
52
|
|
51
53
|
def content_type
|
52
|
-
content_type_for(env[
|
54
|
+
content_type_for(env[Grape::Env::API_FORMAT] || options[:format]) || TEXT_HTML
|
53
55
|
end
|
54
56
|
|
55
57
|
def mime_types
|
56
|
-
|
57
|
-
|
58
|
-
|
58
|
+
types_without_params = {}
|
59
|
+
content_types.each_pair do |k, v|
|
60
|
+
types_without_params[v.split(';').first] = k
|
61
|
+
end
|
62
|
+
types_without_params
|
59
63
|
end
|
60
64
|
end
|
61
65
|
end
|
@@ -47,6 +47,7 @@ module Grape
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def rescuable?(klass)
|
50
|
+
return false if klass == Grape::Exceptions::InvalidVersionHeader
|
50
51
|
options[:rescue_all] || (options[:rescue_handlers] || []).any? { |error, _handler| klass <= error } || (options[:base_only_rescue_handlers] || []).include?(klass)
|
51
52
|
end
|
52
53
|
|
@@ -59,7 +60,7 @@ module Grape
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def error!(message, status = options[:default_status], headers = {}, backtrace = [])
|
62
|
-
headers =
|
63
|
+
headers = headers.reverse_merge(Grape::Http::Headers::CONTENT_TYPE => content_type)
|
63
64
|
rack_response(format_message(message, backtrace), status, headers)
|
64
65
|
end
|
65
66
|
|
@@ -82,7 +83,7 @@ module Grape
|
|
82
83
|
end
|
83
84
|
|
84
85
|
def format_message(message, backtrace)
|
85
|
-
format = env[
|
86
|
+
format = env[Grape::Env::API_FORMAT] || options[:format]
|
86
87
|
formatter = Grape::ErrorFormatter::Base.formatter_for(format, options)
|
87
88
|
throw :error, status: 406, message: "The requested format '#{format}' is not supported." unless formatter
|
88
89
|
formatter.call(message, backtrace, options, env)
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
# This is a simple middleware for adding before and after filters
|
4
4
|
# to Grape APIs. It is used like so:
|
5
5
|
#
|
6
|
-
# use Grape::Middleware::Filter, before:
|
6
|
+
# use Grape::Middleware::Filter, before: -> { do_something }, after: -> { do_something }
|
7
7
|
class Filter < Base
|
8
8
|
def before
|
9
9
|
app.instance_eval(&options[:before]) if options[:before]
|