grape 0.3.0 → 0.7.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 +15 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +70 -0
- data/.travis.yml +7 -6
- data/CHANGELOG.md +134 -4
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +5 -2
- data/README.md +551 -116
- data/RELEASING.md +105 -0
- data/Rakefile +29 -8
- data/UPGRADING.md +124 -0
- data/grape.gemspec +3 -3
- data/lib/grape/api.rb +207 -88
- data/lib/grape/cookies.rb +4 -8
- data/lib/grape/endpoint.rb +198 -144
- data/lib/grape/error_formatter/base.rb +5 -7
- data/lib/grape/error_formatter/json.rb +3 -5
- data/lib/grape/error_formatter/txt.rb +1 -3
- data/lib/grape/error_formatter/xml.rb +4 -6
- data/lib/grape/exceptions/base.rb +9 -9
- data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
- data/lib/grape/exceptions/invalid_formatter.rb +1 -4
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -5
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -6
- data/lib/grape/exceptions/missing_mime_type.rb +1 -5
- data/lib/grape/exceptions/missing_option.rb +1 -4
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -4
- data/lib/grape/exceptions/unknown_options.rb +1 -5
- data/lib/grape/exceptions/unknown_validator.rb +1 -3
- data/lib/grape/exceptions/validation.rb +13 -3
- data/lib/grape/exceptions/validation_errors.rb +43 -0
- data/lib/grape/formatter/base.rb +5 -7
- data/lib/grape/formatter/json.rb +0 -3
- data/lib/grape/formatter/serializable_hash.rb +15 -15
- data/lib/grape/formatter/txt.rb +0 -2
- data/lib/grape/formatter/xml.rb +0 -2
- data/lib/grape/http/request.rb +26 -0
- data/lib/grape/locale/en.yml +8 -5
- data/lib/grape/middleware/auth/base.rb +30 -0
- data/lib/grape/middleware/auth/basic.rb +3 -20
- data/lib/grape/middleware/auth/digest.rb +2 -19
- data/lib/grape/middleware/auth/oauth2.rb +31 -24
- data/lib/grape/middleware/base.rb +7 -7
- data/lib/grape/middleware/error.rb +36 -22
- data/lib/grape/middleware/filter.rb +3 -3
- data/lib/grape/middleware/formatter.rb +99 -61
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +22 -16
- data/lib/grape/middleware/versioner/param.rb +9 -11
- data/lib/grape/middleware/versioner/path.rb +10 -13
- data/lib/grape/middleware/versioner.rb +3 -1
- data/lib/grape/namespace.rb +23 -0
- data/lib/grape/parser/base.rb +3 -5
- data/lib/grape/parser/json.rb +0 -2
- data/lib/grape/parser/xml.rb +0 -2
- data/lib/grape/path.rb +70 -0
- data/lib/grape/route.rb +10 -6
- data/lib/grape/util/content_types.rb +2 -1
- data/lib/grape/util/deep_merge.rb +5 -5
- data/lib/grape/util/hash_stack.rb +13 -2
- data/lib/grape/validations/coerce.rb +11 -10
- data/lib/grape/validations/default.rb +25 -0
- data/lib/grape/validations/presence.rb +7 -3
- data/lib/grape/validations/regexp.rb +2 -5
- data/lib/grape/validations/values.rb +17 -0
- data/lib/grape/validations.rb +161 -54
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +19 -4
- data/spec/grape/api_spec.rb +897 -268
- data/spec/grape/endpoint_spec.rb +283 -66
- data/spec/grape/entity_spec.rb +132 -29
- data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/basic_spec.rb +8 -8
- data/spec/grape/middleware/auth/digest_spec.rb +5 -5
- data/spec/grape/middleware/auth/oauth2_spec.rb +81 -36
- data/spec/grape/middleware/base_spec.rb +8 -13
- data/spec/grape/middleware/error_spec.rb +13 -17
- data/spec/grape/middleware/exception_spec.rb +47 -27
- data/spec/grape/middleware/formatter_spec.rb +103 -41
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner/header_spec.rb +76 -51
- data/spec/grape/middleware/versioner/param_spec.rb +18 -18
- data/spec/grape/middleware/versioner/path_spec.rb +6 -6
- data/spec/grape/middleware/versioner_spec.rb +5 -2
- data/spec/grape/path_spec.rb +229 -0
- data/spec/grape/util/hash_stack_spec.rb +31 -32
- data/spec/grape/validations/coerce_spec.rb +116 -51
- data/spec/grape/validations/default_spec.rb +123 -0
- data/spec/grape/validations/presence_spec.rb +42 -44
- data/spec/grape/validations/regexp_spec.rb +9 -9
- data/spec/grape/validations/values_spec.rb +138 -0
- data/spec/grape/validations/zh-CN.yml +4 -3
- data/spec/grape/validations_spec.rb +681 -48
- data/spec/shared/versioning_examples.rb +22 -6
- data/spec/spec_helper.rb +3 -2
- data/spec/support/basic_auth_encode_helpers.rb +0 -1
- data/spec/support/content_type_helpers.rb +11 -0
- data/spec/support/versioned_helpers.rb +13 -5
- metadata +34 -84
data/lib/grape/api.rb
CHANGED
@@ -6,14 +6,10 @@ module Grape
|
|
6
6
|
extend Validations::ClassMethods
|
7
7
|
|
8
8
|
class << self
|
9
|
-
attr_reader :route_set
|
10
|
-
attr_reader :versions
|
11
|
-
attr_reader :routes
|
12
|
-
attr_reader :settings
|
9
|
+
attr_reader :endpoints, :instance, :routes, :route_set, :settings, :versions
|
13
10
|
attr_writer :logger
|
14
|
-
|
15
|
-
|
16
|
-
attr_reader :instance
|
11
|
+
|
12
|
+
LOCK = Mutex.new
|
17
13
|
|
18
14
|
def logger(logger = nil)
|
19
15
|
if logger
|
@@ -27,13 +23,12 @@ module Grape
|
|
27
23
|
@settings = Grape::Util::HashStack.new
|
28
24
|
@route_set = Rack::Mount::RouteSet.new
|
29
25
|
@endpoints = []
|
30
|
-
@mountings = []
|
31
26
|
@routes = nil
|
32
27
|
reset_validations!
|
33
28
|
end
|
34
29
|
|
35
30
|
def compile
|
36
|
-
@instance
|
31
|
+
@instance ||= new
|
37
32
|
end
|
38
33
|
|
39
34
|
def change!
|
@@ -41,7 +36,7 @@ module Grape
|
|
41
36
|
end
|
42
37
|
|
43
38
|
def call(env)
|
44
|
-
compile unless instance
|
39
|
+
LOCK.synchronize { compile } unless instance
|
45
40
|
call!(env)
|
46
41
|
end
|
47
42
|
|
@@ -88,12 +83,12 @@ module Grape
|
|
88
83
|
# version 'v2'
|
89
84
|
#
|
90
85
|
# get '/main' do
|
91
|
-
# {:
|
86
|
+
# {some: 'data'}
|
92
87
|
# end
|
93
88
|
#
|
94
89
|
# version 'v1' do
|
95
90
|
# get '/main' do
|
96
|
-
# {:
|
91
|
+
# {legacy: 'data'}
|
97
92
|
# end
|
98
93
|
# end
|
99
94
|
# end
|
@@ -102,7 +97,7 @@ module Grape
|
|
102
97
|
if args.any?
|
103
98
|
options = args.pop if args.last.is_a? Hash
|
104
99
|
options ||= {}
|
105
|
-
options = {:
|
100
|
+
options = { using: :path }.merge(options)
|
106
101
|
|
107
102
|
raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.has_key?(:vendor)
|
108
103
|
|
@@ -118,7 +113,7 @@ module Grape
|
|
118
113
|
|
119
114
|
# Add a description to the next namespace or function.
|
120
115
|
def desc(description, options = {})
|
121
|
-
@last_description = options.merge(:
|
116
|
+
@last_description = options.merge(description: description)
|
122
117
|
end
|
123
118
|
|
124
119
|
# Specify the default format for the API's serializers.
|
@@ -154,12 +149,23 @@ module Grape
|
|
154
149
|
end
|
155
150
|
|
156
151
|
# Specify a default error formatter.
|
157
|
-
def default_error_formatter(
|
158
|
-
|
152
|
+
def default_error_formatter(new_formatter_name = nil)
|
153
|
+
if new_formatter_name
|
154
|
+
new_formatter = Grape::ErrorFormatter::Base.formatter_for(new_formatter_name, {})
|
155
|
+
set(:default_error_formatter, new_formatter)
|
156
|
+
else
|
157
|
+
settings[:default_error_formatter]
|
158
|
+
end
|
159
159
|
end
|
160
160
|
|
161
|
-
def error_formatter(format,
|
162
|
-
|
161
|
+
def error_formatter(format, options)
|
162
|
+
if options.is_a?(Hash) && options.has_key?(:with)
|
163
|
+
formatter = options[:with]
|
164
|
+
else
|
165
|
+
formatter = options
|
166
|
+
end
|
167
|
+
|
168
|
+
settings.imbue(:error_formatters, format.to_sym => formatter)
|
163
169
|
end
|
164
170
|
|
165
171
|
# Specify additional content-types, e.g.:
|
@@ -195,15 +201,25 @@ module Grape
|
|
195
201
|
# @param [Block] block Execution block to handle the given exception.
|
196
202
|
# @param [Hash] options Options for the rescue usage.
|
197
203
|
# @option options [Boolean] :backtrace Include a backtrace in the rescue response.
|
204
|
+
# @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
|
205
|
+
# @param [Proc] handler Execution proc to handle the given exception as an
|
206
|
+
# alternative to passing a block
|
198
207
|
def rescue_from(*args, &block)
|
199
|
-
if
|
200
|
-
args.
|
201
|
-
|
202
|
-
|
208
|
+
if args.last.is_a?(Proc)
|
209
|
+
handler = args.pop
|
210
|
+
elsif block_given?
|
211
|
+
handler = block
|
203
212
|
end
|
204
|
-
|
205
|
-
|
206
|
-
|
213
|
+
|
214
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
215
|
+
handler ||= proc { options[:with] } if options.has_key?(:with)
|
216
|
+
|
217
|
+
handler_type = !!options[:rescue_subclasses] ? :rescue_handlers : :base_only_rescue_handlers
|
218
|
+
imbue handler_type, Hash[args.map { |arg| [arg, handler] }]
|
219
|
+
|
220
|
+
imbue(:rescue_options, options)
|
221
|
+
|
222
|
+
set(:rescue_all, true) if args.include?(:all)
|
207
223
|
end
|
208
224
|
|
209
225
|
# Allows you to specify a default representation entity for a
|
@@ -212,10 +228,10 @@ module Grape
|
|
212
228
|
#
|
213
229
|
# @example
|
214
230
|
# class ExampleAPI < Grape::API
|
215
|
-
# represent User, :
|
231
|
+
# represent User, with: Entity::User
|
216
232
|
#
|
217
233
|
# get '/me' do
|
218
|
-
# present current_user # :
|
234
|
+
# present current_user # with: Entity::User is assumed
|
219
235
|
# end
|
220
236
|
# end
|
221
237
|
#
|
@@ -254,11 +270,16 @@ module Grape
|
|
254
270
|
if block_given? || new_mod
|
255
271
|
mod = settings.peek[:helpers] || Module.new
|
256
272
|
if new_mod
|
273
|
+
inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(Helpers)
|
257
274
|
mod.class_eval do
|
258
275
|
include new_mod
|
259
276
|
end
|
260
277
|
end
|
261
|
-
|
278
|
+
if block_given?
|
279
|
+
inject_api_helpers_to_mod(mod) do
|
280
|
+
mod.class_eval(&block)
|
281
|
+
end
|
282
|
+
end
|
262
283
|
set(:helpers, mod)
|
263
284
|
else
|
264
285
|
mod = Module.new
|
@@ -274,7 +295,7 @@ module Grape
|
|
274
295
|
# only `:http_basic`, `:http_digest` and `:oauth2` are supported.
|
275
296
|
def auth(type = nil, options = {}, &block)
|
276
297
|
if type
|
277
|
-
set(:auth, {:
|
298
|
+
set(:auth, { type: type.to_sym, proc: block }.merge(options))
|
278
299
|
else
|
279
300
|
settings[:auth]
|
280
301
|
end
|
@@ -298,17 +319,18 @@ module Grape
|
|
298
319
|
def mount(mounts)
|
299
320
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
300
321
|
mounts.each_pair do |app, path|
|
301
|
-
if app.respond_to?(:inherit_settings)
|
322
|
+
if app.respond_to?(:inherit_settings, true)
|
302
323
|
app_settings = settings.clone
|
303
|
-
mount_path = Rack::Mount::Utils.normalize_path([
|
324
|
+
mount_path = Rack::Mount::Utils.normalize_path([settings[:mount_path], path].compact.join("/"))
|
304
325
|
app_settings.set :mount_path, mount_path
|
305
326
|
app.inherit_settings(app_settings)
|
306
327
|
end
|
307
|
-
endpoints << Grape::Endpoint.new(
|
308
|
-
|
309
|
-
:
|
310
|
-
:
|
311
|
-
|
328
|
+
endpoints << Grape::Endpoint.new(
|
329
|
+
settings.clone,
|
330
|
+
method: :any,
|
331
|
+
path: path,
|
332
|
+
app: app
|
333
|
+
)
|
312
334
|
end
|
313
335
|
end
|
314
336
|
|
@@ -321,14 +343,14 @@ module Grape
|
|
321
343
|
# @example Defining a basic route.
|
322
344
|
# class MyAPI < Grape::API
|
323
345
|
# route(:any, '/hello') do
|
324
|
-
# {:
|
346
|
+
# {hello: 'world'}
|
325
347
|
# end
|
326
348
|
# end
|
327
349
|
def route(methods, paths = ['/'], route_options = {}, &block)
|
328
350
|
endpoint_options = {
|
329
|
-
:
|
330
|
-
:
|
331
|
-
:
|
351
|
+
method: methods,
|
352
|
+
path: paths,
|
353
|
+
route_options: (@namespace_description || {}).deep_merge(@last_description || {}).deep_merge(route_options || {})
|
332
354
|
}
|
333
355
|
endpoints << Grape::Endpoint.new(settings.clone, endpoint_options, &block)
|
334
356
|
|
@@ -340,6 +362,10 @@ module Grape
|
|
340
362
|
imbue(:befores, [block])
|
341
363
|
end
|
342
364
|
|
365
|
+
def before_validation(&block)
|
366
|
+
imbue(:before_validations, [block])
|
367
|
+
end
|
368
|
+
|
343
369
|
def after_validation(&block)
|
344
370
|
imbue(:after_validations, [block])
|
345
371
|
end
|
@@ -348,28 +374,59 @@ module Grape
|
|
348
374
|
imbue(:afters, [block])
|
349
375
|
end
|
350
376
|
|
351
|
-
def get(paths = ['/'], options = {}, &block)
|
352
|
-
|
353
|
-
|
354
|
-
def head(paths = ['/'], options = {}, &block); route('HEAD', paths, options, &block) end
|
355
|
-
def delete(paths = ['/'], options = {}, &block); route('DELETE', paths, options, &block) end
|
356
|
-
def options(paths = ['/'], options = {}, &block); route('OPTIONS', paths, options, &block) end
|
357
|
-
def patch(paths = ['/'], options = {}, &block); route('PATCH', paths, options, &block) end
|
377
|
+
def get(paths = ['/'], options = {}, &block)
|
378
|
+
route('GET', paths, options, &block)
|
379
|
+
end
|
358
380
|
|
359
|
-
def
|
381
|
+
def post(paths = ['/'], options = {}, &block)
|
382
|
+
route('POST', paths, options, &block)
|
383
|
+
end
|
384
|
+
|
385
|
+
def put(paths = ['/'], options = {}, &block)
|
386
|
+
route('PUT', paths, options, &block)
|
387
|
+
end
|
388
|
+
|
389
|
+
def head(paths = ['/'], options = {}, &block)
|
390
|
+
route('HEAD', paths, options, &block)
|
391
|
+
end
|
392
|
+
|
393
|
+
def delete(paths = ['/'], options = {}, &block)
|
394
|
+
route('DELETE', paths, options, &block)
|
395
|
+
end
|
396
|
+
|
397
|
+
def options(paths = ['/'], options = {}, &block)
|
398
|
+
route('OPTIONS', paths, options, &block)
|
399
|
+
end
|
400
|
+
|
401
|
+
def patch(paths = ['/'], options = {}, &block)
|
402
|
+
route('PATCH', paths, options, &block)
|
403
|
+
end
|
404
|
+
|
405
|
+
def namespace(space = nil, options = {}, &block)
|
360
406
|
if space || block_given?
|
361
407
|
previous_namespace_description = @namespace_description
|
362
408
|
@namespace_description = (@namespace_description || {}).deep_merge(@last_description || {})
|
363
409
|
@last_description = nil
|
364
410
|
nest(block) do
|
365
|
-
set(:namespace, space
|
411
|
+
set(:namespace, Namespace.new(space, options)) if space
|
366
412
|
end
|
367
413
|
@namespace_description = previous_namespace_description
|
368
414
|
else
|
369
|
-
|
415
|
+
Namespace.joined_space_path(settings)
|
370
416
|
end
|
371
417
|
end
|
372
418
|
|
419
|
+
# Thie method allows you to quickly define a parameter route segment
|
420
|
+
# in your API.
|
421
|
+
#
|
422
|
+
# @param param [Symbol] The name of the parameter you wish to declare.
|
423
|
+
# @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
|
424
|
+
def route_param(param, options = {}, &block)
|
425
|
+
options = options.dup
|
426
|
+
options[:requirements] = { param.to_sym => options[:requirements] } if options[:requirements].is_a?(Regexp)
|
427
|
+
namespace(":#{param}", options, &block)
|
428
|
+
end
|
429
|
+
|
373
430
|
alias_method :group, :namespace
|
374
431
|
alias_method :resource, :namespace
|
375
432
|
alias_method :resources, :namespace
|
@@ -398,7 +455,10 @@ module Grape
|
|
398
455
|
# and arguments that are currently applied to the
|
399
456
|
# application.
|
400
457
|
def middleware
|
401
|
-
settings.stack.inject([])
|
458
|
+
settings.stack.inject([]) do |a, s|
|
459
|
+
a += s[:middleware] if s[:middleware]
|
460
|
+
a
|
461
|
+
end
|
402
462
|
end
|
403
463
|
|
404
464
|
# An array of API routes.
|
@@ -410,6 +470,14 @@ module Grape
|
|
410
470
|
@versions ||= []
|
411
471
|
end
|
412
472
|
|
473
|
+
def cascade(value = nil)
|
474
|
+
if value.nil?
|
475
|
+
settings.has_key?(:cascade) ? !!settings[:cascade] : true
|
476
|
+
else
|
477
|
+
set(:cascade, value)
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
413
481
|
protected
|
414
482
|
|
415
483
|
def prepare_routes
|
@@ -424,15 +492,15 @@ module Grape
|
|
424
492
|
# block passed in. Allows for simple 'before' setups
|
425
493
|
# of settings stack pushes.
|
426
494
|
def nest(*blocks, &block)
|
427
|
-
blocks.reject!{|b| b.nil?}
|
495
|
+
blocks.reject! { |b| b.nil? }
|
428
496
|
if blocks.any?
|
429
497
|
settings.push # create a new context to eval the follow
|
430
|
-
instance_eval
|
431
|
-
blocks.each{|b| instance_eval
|
432
|
-
settings.pop
|
498
|
+
instance_eval(&block) if block_given?
|
499
|
+
blocks.each { |b| instance_eval(&b) }
|
500
|
+
settings.pop # when finished, we pop the context
|
433
501
|
reset_validations!
|
434
502
|
else
|
435
|
-
instance_eval
|
503
|
+
instance_eval(&block)
|
436
504
|
end
|
437
505
|
end
|
438
506
|
|
@@ -443,23 +511,32 @@ module Grape
|
|
443
511
|
|
444
512
|
def inherit_settings(other_stack)
|
445
513
|
settings.prepend other_stack
|
446
|
-
endpoints.each
|
514
|
+
endpoints.each do |e|
|
515
|
+
e.settings.prepend(other_stack)
|
516
|
+
e.options[:app].inherit_settings(other_stack) if e.options[:app].respond_to?(:inherit_settings, true)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def inject_api_helpers_to_mod(mod, &block)
|
521
|
+
mod.extend(Helpers)
|
522
|
+
yield if block_given?
|
523
|
+
mod.api_changed(self)
|
447
524
|
end
|
448
525
|
end
|
449
526
|
|
450
527
|
def initialize
|
451
528
|
@route_set = Rack::Mount::RouteSet.new
|
529
|
+
add_head_not_allowed_methods_and_options_methods
|
452
530
|
self.class.endpoints.each do |endpoint|
|
453
531
|
endpoint.mount_in(@route_set)
|
454
532
|
end
|
455
|
-
add_head_not_allowed_methods
|
456
533
|
@route_set.freeze
|
457
534
|
end
|
458
535
|
|
459
536
|
def call(env)
|
460
537
|
status, headers, body = @route_set.call(env)
|
461
538
|
headers.delete('X-Cascade') unless cascade?
|
462
|
-
[
|
539
|
+
[status, headers, body]
|
463
540
|
end
|
464
541
|
|
465
542
|
# Some requests may return a HTTP 404 error if grape cannot find a matching
|
@@ -471,8 +548,9 @@ module Grape
|
|
471
548
|
# errors from reaching upstream. This is effectivelly done by unsetting
|
472
549
|
# X-Cascade. Default :cascade is true.
|
473
550
|
def cascade?
|
474
|
-
|
475
|
-
cascade.
|
551
|
+
return !!self.class.settings[:cascade] if self.class.settings.has_key?(:cascade)
|
552
|
+
return !!self.class.settings[:version_options][:cascade] if self.class.settings[:version_options] && self.class.settings[:version_options].has_key?(:cascade)
|
553
|
+
true
|
476
554
|
end
|
477
555
|
|
478
556
|
reset!
|
@@ -483,37 +561,78 @@ module Grape
|
|
483
561
|
# with a list of HTTP methods that can be called. Also add a route that
|
484
562
|
# will return an HTTP 405 response for any HTTP method that the resource
|
485
563
|
# cannot handle.
|
486
|
-
def
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
resources.flatten.each do |route|
|
494
|
-
allowed_methods[route.route_compiled] << route.route_method
|
495
|
-
end
|
496
|
-
allowed_methods.each do |path_info, methods|
|
497
|
-
if methods.include?('GET') && ! methods.include?("HEAD") && ! self.class.settings[:do_not_route_head]
|
498
|
-
methods = methods | [ 'HEAD' ]
|
499
|
-
end
|
500
|
-
allow_header = (["OPTIONS"] | methods).join(", ")
|
501
|
-
unless methods.include?("OPTIONS") || self.class.settings[:do_not_route_options]
|
502
|
-
@route_set.add_route( proc { [204, { 'Allow' => allow_header }, []]}, {
|
503
|
-
:path_info => path_info,
|
504
|
-
:request_method => "OPTIONS"
|
505
|
-
})
|
564
|
+
def add_head_not_allowed_methods_and_options_methods
|
565
|
+
methods_per_path = {}
|
566
|
+
self.class.endpoints.each do |endpoint|
|
567
|
+
routes = endpoint.routes
|
568
|
+
routes.each do |route|
|
569
|
+
methods_per_path[route.route_path] ||= []
|
570
|
+
methods_per_path[route.route_path] << route.route_method
|
506
571
|
end
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
572
|
+
end
|
573
|
+
|
574
|
+
# The paths we collected are prepared (cf. Path#prepare), so they
|
575
|
+
# contain already versioning information when using path versioning.
|
576
|
+
# Disable versioning so adding a route won't prepend versioning
|
577
|
+
# informations again.
|
578
|
+
without_versioning do
|
579
|
+
methods_per_path.each do |path, methods|
|
580
|
+
allowed_methods = methods.dup
|
581
|
+
unless self.class.settings[:do_not_route_head]
|
582
|
+
if allowed_methods.include?('GET')
|
583
|
+
allowed_methods = allowed_methods | ['HEAD']
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
allow_header = (['OPTIONS'] | allowed_methods).join(', ')
|
588
|
+
unless self.class.settings[:do_not_route_options]
|
589
|
+
unless allowed_methods.include?('OPTIONS')
|
590
|
+
self.class.options(path, {}) do
|
591
|
+
header 'Allow', allow_header
|
592
|
+
status 204
|
593
|
+
''
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - allowed_methods
|
599
|
+
not_allowed_methods << 'OPTIONS' if self.class.settings[:do_not_route_options]
|
600
|
+
self.class.route(not_allowed_methods, path) do
|
601
|
+
header 'Allow', allow_header
|
602
|
+
status 405
|
603
|
+
''
|
604
|
+
end
|
514
605
|
end
|
515
606
|
end
|
516
607
|
end
|
517
608
|
|
609
|
+
def without_versioning(&block)
|
610
|
+
self.class.settings.push(version: nil, version_options: nil)
|
611
|
+
yield
|
612
|
+
self.class.settings.pop
|
613
|
+
end
|
614
|
+
|
615
|
+
# This module extends user defined helpers
|
616
|
+
# to provide some API-specific functionality
|
617
|
+
module Helpers
|
618
|
+
attr_accessor :api
|
619
|
+
def params(name, &block)
|
620
|
+
@named_params ||= {}
|
621
|
+
@named_params.merge! name => block
|
622
|
+
end
|
623
|
+
|
624
|
+
def api_changed(new_api)
|
625
|
+
@api = new_api
|
626
|
+
process_named_params
|
627
|
+
end
|
628
|
+
|
629
|
+
protected
|
630
|
+
|
631
|
+
def process_named_params
|
632
|
+
if @named_params && @named_params.any?
|
633
|
+
api.imbue(:named_params, @named_params)
|
634
|
+
end
|
635
|
+
end
|
636
|
+
end
|
518
637
|
end
|
519
638
|
end
|
data/lib/grape/cookies.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Grape
|
2
2
|
class Cookies
|
3
|
-
|
4
3
|
def initialize
|
5
4
|
@cookies = {}
|
6
5
|
@send_cookies = {}
|
@@ -13,12 +12,10 @@ module Grape
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def write(header)
|
16
|
-
@cookies.select { |key, value|
|
17
|
-
|
18
|
-
}.each { |name, value|
|
19
|
-
cookie_value = value.is_a?(Hash) ? value : { :value => value }
|
15
|
+
@cookies.select { |key, value| @send_cookies[key] == true }.each do |name, value|
|
16
|
+
cookie_value = value.is_a?(Hash) ? value : { value: value }
|
20
17
|
Rack::Utils.set_cookie_header! header, name, cookie_value
|
21
|
-
|
18
|
+
end
|
22
19
|
end
|
23
20
|
|
24
21
|
def [](name)
|
@@ -35,9 +32,8 @@ module Grape
|
|
35
32
|
end
|
36
33
|
|
37
34
|
def delete(name, opts = {})
|
38
|
-
options = opts.merge(
|
35
|
+
options = opts.merge(value: 'deleted', expires: Time.at(0))
|
39
36
|
self.[]=(name, options)
|
40
37
|
end
|
41
|
-
|
42
38
|
end
|
43
39
|
end
|