grape 1.1.0 → 1.2.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.
- checksums.yaml +4 -4
- data/Appraisals +3 -7
- data/CHANGELOG.md +21 -1
- data/Gemfile.lock +26 -26
- data/README.md +109 -29
- data/UPGRADING.md +45 -0
- data/gemfiles/rack_1.5.2.gemfile.lock +232 -0
- data/gemfiles/rails_3.gemfile +1 -1
- data/gemfiles/rails_3.gemfile.lock +288 -0
- data/gemfiles/rails_4.gemfile +1 -1
- data/gemfiles/rails_4.gemfile.lock +280 -0
- data/gemfiles/rails_5.gemfile +1 -1
- data/gemfiles/rails_5.gemfile.lock +312 -0
- data/lib/grape.rb +1 -0
- data/lib/grape/api.rb +74 -195
- data/lib/grape/api/instance.rb +242 -0
- data/lib/grape/dsl/desc.rb +17 -1
- data/lib/grape/dsl/middleware.rb +7 -0
- data/lib/grape/dsl/parameters.rb +9 -4
- data/lib/grape/dsl/routing.rb +5 -1
- data/lib/grape/endpoint.rb +1 -1
- data/lib/grape/exceptions/base.rb +9 -1
- data/lib/grape/exceptions/invalid_response.rb +9 -0
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/error.rb +8 -2
- data/lib/grape/middleware/versioner/header.rb +2 -2
- data/lib/grape/validations/params_scope.rb +1 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/pkg/grape-1.2.0.gem +0 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api_remount_spec.rb +85 -0
- data/spec/grape/api_spec.rb +70 -1
- data/spec/grape/dsl/desc_spec.rb +17 -1
- data/spec/grape/dsl/middleware_spec.rb +8 -0
- data/spec/grape/dsl/routing_spec.rb +10 -0
- data/spec/grape/exceptions/base_spec.rb +61 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
- data/spec/grape/middleware/exception_spec.rb +1 -1
- data/spec/grape/middleware/versioner/header_spec.rb +6 -0
- data/spec/grape/validations/params_scope_spec.rb +133 -0
- data/spec/spec_helper.rb +3 -1
- metadata +99 -87
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
data/lib/grape.rb
CHANGED
data/lib/grape/api.rb
CHANGED
@@ -1,233 +1,112 @@
|
|
1
1
|
require 'grape/router'
|
2
|
+
require 'grape/api/instance'
|
2
3
|
|
3
4
|
module Grape
|
4
5
|
# The API class is the primary entry point for creating Grape APIs. Users
|
5
6
|
# should subclass this class in order to build an API.
|
6
7
|
class API
|
7
|
-
|
8
|
+
# Class methods that we want to call on the API rather than on the API object
|
9
|
+
NON_OVERRIDABLE = %I[define_singleton_method instance_variable_set inspect class is_a? ! kind_of?
|
10
|
+
respond_to? respond_to_missing? const_defined? const_missing parent
|
11
|
+
parent_name name equal? to_s parents anonymous?].freeze
|
8
12
|
|
9
13
|
class << self
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def reset!
|
18
|
-
reset_endpoints!
|
19
|
-
reset_routes!
|
20
|
-
reset_validations!
|
21
|
-
end
|
22
|
-
|
23
|
-
# Parses the API's definition and compiles it into an instance of
|
24
|
-
# Grape::API.
|
25
|
-
def compile
|
26
|
-
@instance ||= new
|
27
|
-
end
|
28
|
-
|
29
|
-
# Wipe the compiled API so we can recompile after changes were made.
|
30
|
-
def change!
|
31
|
-
@instance = nil
|
14
|
+
attr_accessor :base_instance, :instances
|
15
|
+
# When inherited, will create a list of all instances (times the API was mounted)
|
16
|
+
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
17
|
+
def inherited(api, base_instance_parent = Grape::API::Instance)
|
18
|
+
api.initial_setup(base_instance_parent)
|
19
|
+
api.override_all_methods!
|
20
|
+
make_inheritable(api)
|
32
21
|
end
|
33
22
|
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
23
|
+
# Initialize the instance variables on the remountable class, and the base_instance
|
24
|
+
# an instance that will be used to create the set up but will not be mounted
|
25
|
+
def initial_setup(base_instance_parent)
|
26
|
+
@instances = []
|
27
|
+
@setup = []
|
28
|
+
@base_parent = base_instance_parent
|
29
|
+
@base_instance = mount_instance
|
41
30
|
end
|
42
31
|
|
43
|
-
#
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def cascade(value = nil)
|
50
|
-
if value.nil?
|
51
|
-
inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
|
52
|
-
else
|
53
|
-
namespace_inheritable(:cascade, value)
|
32
|
+
# Redefines all methods so that are forwarded to add_setup and be recorded
|
33
|
+
def override_all_methods!
|
34
|
+
(base_instance.methods - NON_OVERRIDABLE).each do |method_override|
|
35
|
+
define_singleton_method(method_override) do |*args, &block|
|
36
|
+
add_setup(method_override, *args, &block)
|
37
|
+
end
|
54
38
|
end
|
55
39
|
end
|
56
40
|
|
57
|
-
#
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def prepare_routes
|
66
|
-
endpoints.map(&:routes).flatten
|
41
|
+
# Allows an API to itself be inheritable:
|
42
|
+
def make_inheritable(api)
|
43
|
+
# When a child API inherits from a parent API.
|
44
|
+
def api.inherited(child_api)
|
45
|
+
# The instances of the child API inherit from the instances of the parent API
|
46
|
+
Grape::API.inherited(child_api, base_instance)
|
47
|
+
end
|
67
48
|
end
|
68
49
|
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
instance_eval(&block) if block_given?
|
76
|
-
blocks.each { |b| instance_eval(&b) }
|
77
|
-
reset_validations!
|
50
|
+
# Alleviates problems with autoloading by tring to search for the constant
|
51
|
+
def const_missing(*args)
|
52
|
+
if base_instance.const_defined?(*args)
|
53
|
+
base_instance.const_get(*args)
|
54
|
+
elsif parent && parent.const_defined?(*args)
|
55
|
+
parent.const_get(*args)
|
78
56
|
else
|
79
|
-
|
57
|
+
super
|
80
58
|
end
|
81
59
|
end
|
82
60
|
|
83
|
-
|
84
|
-
|
85
|
-
|
61
|
+
# The remountable class can have a configuration hash to provide some dynamic class-level variables.
|
62
|
+
# For instance, a descripcion could be done using: `desc configuration[:description]` if it may vary
|
63
|
+
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
|
64
|
+
# too much, you may actually want to provide a new API rather than remount it.
|
65
|
+
def mount_instance(opts = {})
|
66
|
+
instance = Class.new(@base_parent)
|
67
|
+
instance.configuration = opts[:configuration] || {}
|
68
|
+
instance.base = self
|
69
|
+
replay_setup_on(instance)
|
70
|
+
instance
|
86
71
|
end
|
87
72
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
endpoints.each do |e|
|
94
|
-
e.inherit_settings(top_level_setting.namespace_stackable)
|
95
|
-
e.reset_routes!
|
73
|
+
# Replays the set up to produce an API as defined in this class, can be called
|
74
|
+
# on classes that inherit from Grape::API
|
75
|
+
def replay_setup_on(instance)
|
76
|
+
@setup.each do |setup_stage|
|
77
|
+
instance.send(setup_stage[:method], *setup_stage[:args], &setup_stage[:block])
|
96
78
|
end
|
97
|
-
|
98
|
-
reset_routes!
|
99
79
|
end
|
100
|
-
end
|
101
|
-
|
102
|
-
attr_reader :router
|
103
80
|
|
104
|
-
|
105
|
-
|
106
|
-
def initialize
|
107
|
-
@router = Router.new
|
108
|
-
add_head_not_allowed_methods_and_options_methods
|
109
|
-
self.class.endpoints.each do |endpoint|
|
110
|
-
endpoint.mount_in(@router)
|
81
|
+
def respond_to?(method, include_private = false)
|
82
|
+
super(method, include_private) || base_instance.respond_to?(method, include_private)
|
111
83
|
end
|
112
84
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
# Handle a request. See Rack documentation for what `env` is.
|
118
|
-
def call(env)
|
119
|
-
result = @router.call(env)
|
120
|
-
result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
|
121
|
-
result
|
122
|
-
end
|
123
|
-
|
124
|
-
# Some requests may return a HTTP 404 error if grape cannot find a matching
|
125
|
-
# route. In this case, Grape::Router adds a X-Cascade header to the response
|
126
|
-
# and sets it to 'pass', indicating to grape's parents they should keep
|
127
|
-
# looking for a matching route on other resources.
|
128
|
-
#
|
129
|
-
# In some applications (e.g. mounting grape on rails), one might need to trap
|
130
|
-
# errors from reaching upstream. This is effectivelly done by unsetting
|
131
|
-
# X-Cascade. Default :cascade is true.
|
132
|
-
def cascade?
|
133
|
-
return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
|
134
|
-
return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
|
135
|
-
true
|
136
|
-
end
|
137
|
-
|
138
|
-
reset!
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
# For every resource add a 'OPTIONS' route that returns an HTTP 204 response
|
143
|
-
# with a list of HTTP methods that can be called. Also add a route that
|
144
|
-
# will return an HTTP 405 response for any HTTP method that the resource
|
145
|
-
# cannot handle.
|
146
|
-
def add_head_not_allowed_methods_and_options_methods
|
147
|
-
routes_map = {}
|
148
|
-
|
149
|
-
self.class.endpoints.each do |endpoint|
|
150
|
-
routes = endpoint.routes
|
151
|
-
routes.each do |route|
|
152
|
-
# using the :any shorthand produces [nil] for route methods, substitute all manually
|
153
|
-
route_key = route.pattern.to_regexp
|
154
|
-
routes_map[route_key] ||= {}
|
155
|
-
route_settings = routes_map[route_key]
|
156
|
-
route_settings[:pattern] = route.pattern
|
157
|
-
route_settings[:requirements] = route.requirements
|
158
|
-
route_settings[:path] = route.origin
|
159
|
-
route_settings[:methods] ||= []
|
160
|
-
route_settings[:methods] << route.request_method
|
161
|
-
route_settings[:endpoint] = route.app
|
85
|
+
def respond_to_missing?(method, include_private = false)
|
86
|
+
base_instance.respond_to?(method, include_private)
|
87
|
+
end
|
162
88
|
|
163
|
-
|
164
|
-
|
89
|
+
def method_missing(method, *args, &block)
|
90
|
+
# If there's a missing method, it may be defined on the base_instance instead.
|
91
|
+
if respond_to_missing?(method)
|
92
|
+
base_instance.send(method, *args, &block)
|
93
|
+
else
|
94
|
+
super
|
165
95
|
end
|
166
96
|
end
|
167
97
|
|
168
|
-
|
169
|
-
# contain already versioning information when using path versioning.
|
170
|
-
# Disable versioning so adding a route won't prepend versioning
|
171
|
-
# informations again.
|
172
|
-
without_root_prefix do
|
173
|
-
without_versioning do
|
174
|
-
routes_map.each do |_, config|
|
175
|
-
methods = config[:methods]
|
176
|
-
allowed_methods = methods.dup
|
177
|
-
|
178
|
-
unless self.class.namespace_inheritable(:do_not_route_head)
|
179
|
-
allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
|
180
|
-
end
|
98
|
+
private
|
181
99
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
generate_not_allowed_method(config[:pattern], attributes)
|
190
|
-
end
|
100
|
+
# Adds a new stage to the set up require to get a Grape::API up and running
|
101
|
+
def add_setup(method, *args, &block)
|
102
|
+
setup_stage = { method: method, args: args, block: block }
|
103
|
+
@setup << setup_stage
|
104
|
+
last_response = nil
|
105
|
+
@instances.each do |instance|
|
106
|
+
last_response = instance.send(setup_stage[:method], *setup_stage[:args], &setup_stage[:block])
|
191
107
|
end
|
108
|
+
last_response
|
192
109
|
end
|
193
110
|
end
|
194
|
-
|
195
|
-
# Generate a route that returns an HTTP 405 response for a user defined
|
196
|
-
# path on methods not specified
|
197
|
-
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
|
198
|
-
not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
|
199
|
-
not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
|
200
|
-
|
201
|
-
return if not_allowed_methods.empty?
|
202
|
-
|
203
|
-
@router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
|
204
|
-
end
|
205
|
-
|
206
|
-
# Allows definition of endpoints that ignore the versioning configuration
|
207
|
-
# used by the rest of your API.
|
208
|
-
def without_versioning(&_block)
|
209
|
-
old_version = self.class.namespace_inheritable(:version)
|
210
|
-
old_version_options = self.class.namespace_inheritable(:version_options)
|
211
|
-
|
212
|
-
self.class.namespace_inheritable_to_nil(:version)
|
213
|
-
self.class.namespace_inheritable_to_nil(:version_options)
|
214
|
-
|
215
|
-
yield
|
216
|
-
|
217
|
-
self.class.namespace_inheritable(:version, old_version)
|
218
|
-
self.class.namespace_inheritable(:version_options, old_version_options)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Allows definition of endpoints that ignore the root prefix used by the
|
222
|
-
# rest of your API.
|
223
|
-
def without_root_prefix(&_block)
|
224
|
-
old_prefix = self.class.namespace_inheritable(:root_prefix)
|
225
|
-
|
226
|
-
self.class.namespace_inheritable_to_nil(:root_prefix)
|
227
|
-
|
228
|
-
yield
|
229
|
-
|
230
|
-
self.class.namespace_inheritable(:root_prefix, old_prefix)
|
231
|
-
end
|
232
111
|
end
|
233
112
|
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'grape/router'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
class API
|
5
|
+
# The API Instance class, is the engine behind Grape::API. Each class that inherits
|
6
|
+
# from this will represent a different API instance
|
7
|
+
class Instance
|
8
|
+
include Grape::DSL::API
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :instance
|
12
|
+
attr_reader :base
|
13
|
+
attr_accessor :configuration
|
14
|
+
|
15
|
+
def base=(grape_api)
|
16
|
+
@base = grape_api
|
17
|
+
grape_api.instances << self
|
18
|
+
end
|
19
|
+
|
20
|
+
# A class-level lock to ensure the API is not compiled by multiple
|
21
|
+
# threads simultaneously within the same process.
|
22
|
+
LOCK = Mutex.new
|
23
|
+
|
24
|
+
# Clears all defined routes, endpoints, etc., on this API.
|
25
|
+
def reset!
|
26
|
+
reset_endpoints!
|
27
|
+
reset_routes!
|
28
|
+
reset_validations!
|
29
|
+
end
|
30
|
+
|
31
|
+
# Parses the API's definition and compiles it into an instance of
|
32
|
+
# Grape::API.
|
33
|
+
def compile
|
34
|
+
@instance ||= new
|
35
|
+
end
|
36
|
+
|
37
|
+
# Wipe the compiled API so we can recompile after changes were made.
|
38
|
+
def change!
|
39
|
+
@instance = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# This is the interface point between Rack and Grape; it accepts a request
|
43
|
+
# from Rack and ultimately returns an array of three values: the status,
|
44
|
+
# the headers, and the body. See [the rack specification]
|
45
|
+
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
46
|
+
def call(env)
|
47
|
+
LOCK.synchronize { compile } unless instance
|
48
|
+
call!(env)
|
49
|
+
end
|
50
|
+
|
51
|
+
# A non-synchronized version of ::call.
|
52
|
+
def call!(env)
|
53
|
+
instance.call(env)
|
54
|
+
end
|
55
|
+
|
56
|
+
# (see #cascade?)
|
57
|
+
def cascade(value = nil)
|
58
|
+
if value.nil?
|
59
|
+
inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
|
60
|
+
else
|
61
|
+
namespace_inheritable(:cascade, value)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# see Grape::Router#recognize_path
|
66
|
+
def recognize_path(path)
|
67
|
+
LOCK.synchronize { compile } unless instance
|
68
|
+
instance.router.recognize_path(path)
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def prepare_routes
|
74
|
+
endpoints.map(&:routes).flatten
|
75
|
+
end
|
76
|
+
|
77
|
+
# Execute first the provided block, then each of the
|
78
|
+
# block passed in. Allows for simple 'before' setups
|
79
|
+
# of settings stack pushes.
|
80
|
+
def nest(*blocks, &block)
|
81
|
+
blocks.reject!(&:nil?)
|
82
|
+
if blocks.any?
|
83
|
+
instance_eval(&block) if block_given?
|
84
|
+
blocks.each { |b| instance_eval(&b) }
|
85
|
+
reset_validations!
|
86
|
+
else
|
87
|
+
instance_eval(&block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def inherited(subclass)
|
92
|
+
subclass.reset!
|
93
|
+
subclass.logger = logger.clone
|
94
|
+
end
|
95
|
+
|
96
|
+
def inherit_settings(other_settings)
|
97
|
+
top_level_setting.inherit_from other_settings.point_in_time_copy
|
98
|
+
|
99
|
+
# Propagate any inherited params down to our endpoints, and reset any
|
100
|
+
# compiled routes.
|
101
|
+
endpoints.each do |e|
|
102
|
+
e.inherit_settings(top_level_setting.namespace_stackable)
|
103
|
+
e.reset_routes!
|
104
|
+
end
|
105
|
+
|
106
|
+
reset_routes!
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
attr_reader :router
|
111
|
+
|
112
|
+
# Builds the routes from the defined endpoints, effectively compiling
|
113
|
+
# this API into a usable form.
|
114
|
+
def initialize
|
115
|
+
@router = Router.new
|
116
|
+
add_head_not_allowed_methods_and_options_methods
|
117
|
+
self.class.endpoints.each do |endpoint|
|
118
|
+
endpoint.mount_in(@router)
|
119
|
+
end
|
120
|
+
|
121
|
+
@router.compile!
|
122
|
+
@router.freeze
|
123
|
+
end
|
124
|
+
|
125
|
+
# Handle a request. See Rack documentation for what `env` is.
|
126
|
+
def call(env)
|
127
|
+
result = @router.call(env)
|
128
|
+
result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
# Some requests may return a HTTP 404 error if grape cannot find a matching
|
133
|
+
# route. In this case, Grape::Router adds a X-Cascade header to the response
|
134
|
+
# and sets it to 'pass', indicating to grape's parents they should keep
|
135
|
+
# looking for a matching route on other resources.
|
136
|
+
#
|
137
|
+
# In some applications (e.g. mounting grape on rails), one might need to trap
|
138
|
+
# errors from reaching upstream. This is effectivelly done by unsetting
|
139
|
+
# X-Cascade. Default :cascade is true.
|
140
|
+
def cascade?
|
141
|
+
return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
|
142
|
+
return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
reset!
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
# For every resource add a 'OPTIONS' route that returns an HTTP 204 response
|
151
|
+
# with a list of HTTP methods that can be called. Also add a route that
|
152
|
+
# will return an HTTP 405 response for any HTTP method that the resource
|
153
|
+
# cannot handle.
|
154
|
+
def add_head_not_allowed_methods_and_options_methods
|
155
|
+
routes_map = {}
|
156
|
+
|
157
|
+
self.class.endpoints.each do |endpoint|
|
158
|
+
routes = endpoint.routes
|
159
|
+
routes.each do |route|
|
160
|
+
# using the :any shorthand produces [nil] for route methods, substitute all manually
|
161
|
+
route_key = route.pattern.to_regexp
|
162
|
+
routes_map[route_key] ||= {}
|
163
|
+
route_settings = routes_map[route_key]
|
164
|
+
route_settings[:pattern] = route.pattern
|
165
|
+
route_settings[:requirements] = route.requirements
|
166
|
+
route_settings[:path] = route.origin
|
167
|
+
route_settings[:methods] ||= []
|
168
|
+
route_settings[:methods] << route.request_method
|
169
|
+
route_settings[:endpoint] = route.app
|
170
|
+
|
171
|
+
# using the :any shorthand produces [nil] for route methods, substitute all manually
|
172
|
+
route_settings[:methods] = %w[GET PUT POST DELETE PATCH HEAD OPTIONS] if route_settings[:methods].include?('*')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# The paths we collected are prepared (cf. Path#prepare), so they
|
177
|
+
# contain already versioning information when using path versioning.
|
178
|
+
# Disable versioning so adding a route won't prepend versioning
|
179
|
+
# informations again.
|
180
|
+
without_root_prefix do
|
181
|
+
without_versioning do
|
182
|
+
routes_map.each do |_, config|
|
183
|
+
methods = config[:methods]
|
184
|
+
allowed_methods = methods.dup
|
185
|
+
|
186
|
+
unless self.class.namespace_inheritable(:do_not_route_head)
|
187
|
+
allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
|
188
|
+
end
|
189
|
+
|
190
|
+
allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
|
191
|
+
|
192
|
+
unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
|
193
|
+
config[:endpoint].options[:options_route_enabled] = true
|
194
|
+
end
|
195
|
+
|
196
|
+
attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
|
197
|
+
generate_not_allowed_method(config[:pattern], attributes)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Generate a route that returns an HTTP 405 response for a user defined
|
204
|
+
# path on methods not specified
|
205
|
+
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
|
206
|
+
not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
|
207
|
+
not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
|
208
|
+
|
209
|
+
return if not_allowed_methods.empty?
|
210
|
+
|
211
|
+
@router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Allows definition of endpoints that ignore the versioning configuration
|
215
|
+
# used by the rest of your API.
|
216
|
+
def without_versioning(&_block)
|
217
|
+
old_version = self.class.namespace_inheritable(:version)
|
218
|
+
old_version_options = self.class.namespace_inheritable(:version_options)
|
219
|
+
|
220
|
+
self.class.namespace_inheritable_to_nil(:version)
|
221
|
+
self.class.namespace_inheritable_to_nil(:version_options)
|
222
|
+
|
223
|
+
yield
|
224
|
+
|
225
|
+
self.class.namespace_inheritable(:version, old_version)
|
226
|
+
self.class.namespace_inheritable(:version_options, old_version_options)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Allows definition of endpoints that ignore the root prefix used by the
|
230
|
+
# rest of your API.
|
231
|
+
def without_root_prefix(&_block)
|
232
|
+
old_prefix = self.class.namespace_inheritable(:root_prefix)
|
233
|
+
|
234
|
+
self.class.namespace_inheritable_to_nil(:root_prefix)
|
235
|
+
|
236
|
+
yield
|
237
|
+
|
238
|
+
self.class.namespace_inheritable(:root_prefix, old_prefix)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|