Skip to content

Commit 9f4ba67

Browse files
committed
Replace rack-mount with new router
1 parent 41808e3 commit 9f4ba67

31 files changed

+612
-189
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
0.15.1 (Next)
22
=============
33

4+
#### Features
5+
6+
* [#1276](https://github.com/ruby-grape/grape/pull/1276): Replace rack-mount with new router - [@namusyaka](https://github.com/namusyaka).
47
* Your contribution here.
58

9+
#### Fixes
10+
611
0.15.0 (3/8/2016)
712
=================
813

@@ -32,7 +37,6 @@
3237
* [#1197](https://github.com/ruby-grape/grape/pull/1290): Fix using JSON and Array[JSON] as groups when parameter is optional - [@lukeivers](https://github.com/lukeivers).
3338

3439
0.14.0 (12/07/2015)
35-
===================
3640

3741
#### Features
3842

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2568,11 +2568,15 @@ Examine the routes at runtime.
25682568
```ruby
25692569
TwitterAPI::versions # yields [ 'v1', 'v2' ]
25702570
TwitterAPI::routes # yields an array of Grape::Route objects
2571-
TwitterAPI::routes[0].route_version # => 'v1'
2572-
TwitterAPI::routes[0].route_description # => 'Includes custom settings.'
2573-
TwitterAPI::routes[0].route_settings[:custom] # => { key: 'value' }
2571+
TwitterAPI::routes[0].version # => 'v1'
2572+
TwitterAPI::routes[0].description # => 'Includes custom settings.'
2573+
TwitterAPI::routes[0].settings[:custom] # => { key: 'value' }
25742574
```
25752575

2576+
Note that `Route#route_xyz` methods have been deprecated since 0.15.0.
2577+
2578+
Please use `Route#xyz` instead.
2579+
25762580
## Current Route and Endpoint
25772581

25782582
It's possible to retrieve the information about the current route from within an API call with `route`.

UPGRADING.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
Upgrading Grape
22
===============
33

4+
### Upgrading to >= 0.16.0
5+
6+
#### Replace rack-mount with new router
7+
8+
The `Route#route_xyz` methods have been deprecated since 0.15.1.
9+
10+
Please use `Route#xyz` instead.
11+
12+
Note that the `Route#route_method` was replaced by `Route#request_method`.
13+
14+
The following code would work correctly.
15+
16+
```ruby
17+
TwitterAPI::versions # yields [ 'v1', 'v2' ]
18+
TwitterAPI::routes # yields an array of Grape::Route objects
19+
TwitterAPI::routes[0].version # => 'v1'
20+
TwitterAPI::routes[0].description # => 'Includes custom settings.'
21+
TwitterAPI::routes[0].settings[:custom] # => { key: 'value' }
22+
23+
TwitterAPI::routes[0].request_method # => 'GET'
24+
```
25+
426
### Upgrading to >= 0.15.0
527

628
#### Changes to availability of `:with` option of `rescue_from` method

grape.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
1313
s.license = 'MIT'
1414

1515
s.add_runtime_dependency 'rack', '>= 1.3.0'
16-
s.add_runtime_dependency 'rack-mount'
16+
s.add_runtime_dependency 'mustermann19', '~> 0.4.2'
1717
s.add_runtime_dependency 'rack-accept'
1818
s.add_runtime_dependency 'activesupport'
1919
s.add_runtime_dependency 'multi_json', '>= 1.3.2'

lib/grape.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
require 'logger'
22
require 'rack'
3-
require 'rack/mount'
43
require 'rack/builder'
54
require 'rack/accept'
65
require 'rack/auth/basic'
@@ -33,8 +32,8 @@ module Grape
3332
eager_autoload do
3433
autoload :API
3534
autoload :Endpoint
35+
autoload :Router
3636

37-
autoload :Route
3837
autoload :Namespace
3938

4039
autoload :Path

lib/grape/api.rb

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
require 'grape/router'
2+
13
module Grape
24
# The API class is the primary entry point for creating Grape APIs. Users
35
# should subclass this class in order to build an API.
46
class API
57
include Grape::DSL::API
68

79
class << self
8-
attr_reader :instance
10+
attr_reader :instance, :router
911

1012
# A class-level lock to ensure the API is not compiled by multiple
1113
# threads simultaneously within the same process.
@@ -87,24 +89,25 @@ def inherit_settings(other_settings)
8789
# Builds the routes from the defined endpoints, effectively compiling
8890
# this API into a usable form.
8991
def initialize
90-
@route_set = Rack::Mount::RouteSet.new
92+
@router = Router.new
9193
add_head_not_allowed_methods_and_options_methods
9294
self.class.endpoints.each do |endpoint|
93-
endpoint.mount_in(@route_set)
95+
endpoint.mount_in(@router)
9496
end
9597

96-
@route_set.freeze
98+
@router.compile!
99+
@router.freeze
97100
end
98101

99102
# Handle a request. See Rack documentation for what `env` is.
100103
def call(env)
101-
result = @route_set.call(env)
104+
result = @router.call(env)
102105
result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
103106
result
104107
end
105108

106109
# Some requests may return a HTTP 404 error if grape cannot find a matching
107-
# route. In this case, Rack::Mount adds a X-Cascade header to the response
110+
# route. In this case, Grape::Router adds a X-Cascade header to the response
108111
# and sets it to 'pass', indicating to grape's parents they should keep
109112
# looking for a matching route on other resources.
110113
#
@@ -126,20 +129,23 @@ def cascade?
126129
# will return an HTTP 405 response for any HTTP method that the resource
127130
# cannot handle.
128131
def add_head_not_allowed_methods_and_options_methods
129-
methods_per_path = {}
132+
routes_map = {}
130133

131134
self.class.endpoints.each do |endpoint|
132135
routes = endpoint.routes
133136
routes.each do |route|
134-
route_path = route.route_path
135-
.gsub(/\(.*\)/, '') # ignore any optional portions
136-
.gsub(%r{\:[^\/.?]+}, ':x') # substitute variable names to avoid conflicts
137-
138-
methods_per_path[route_path] ||= []
139-
methods_per_path[route_path] << route.route_method
137+
# using the :any shorthand produces [nil] for route methods, substitute all manually
138+
route_key = route.pattern.to_regexp
139+
routes_map[route_key] ||= {}
140+
route_settings = routes_map[route_key]
141+
route_settings[:requirements] = route.requirements
142+
route_settings[:path] = route.origin
143+
route_settings[:methods] ||= []
144+
route_settings[:methods] << route.request_method
145+
route_settings[:endpoint] = route.app
140146

141147
# using the :any shorthand produces [nil] for route methods, substitute all manually
142-
methods_per_path[route_path] = %w(GET PUT POST DELETE PATCH HEAD OPTIONS) if methods_per_path[route_path].compact.empty?
148+
route_settings[:methods] = %w(GET PUT POST DELETE PATCH HEAD OPTIONS) if route_settings[:methods].include?('ANY')
143149
end
144150
end
145151

@@ -149,7 +155,9 @@ def add_head_not_allowed_methods_and_options_methods
149155
# informations again.
150156
without_root_prefix do
151157
without_versioning do
152-
methods_per_path.each do |path, methods|
158+
routes_map.each do |regexp, config|
159+
methods = config[:methods]
160+
path = config[:path]
153161
allowed_methods = methods.dup
154162

155163
unless self.class.namespace_inheritable(:do_not_route_head)
@@ -159,18 +167,18 @@ def add_head_not_allowed_methods_and_options_methods
159167
allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
160168

161169
unless self.class.namespace_inheritable(:do_not_route_options)
162-
generate_options_method(path, allow_header) unless allowed_methods.include?(Grape::Http::Headers::OPTIONS)
170+
generate_options_method(path, allow_header, config) unless allowed_methods.include?(Grape::Http::Headers::OPTIONS)
163171
end
164172

165-
generate_not_allowed_method(path, allowed_methods, allow_header)
173+
generate_not_allowed_method(regexp, allowed_methods, allow_header, config[:endpoint])
166174
end
167175
end
168176
end
169177
end
170178

171179
# Generate an 'OPTIONS' route for a pre-exisiting user defined route
172-
def generate_options_method(path, allow_header)
173-
self.class.options(path, {}) do
180+
def generate_options_method(path, allow_header, options = {})
181+
self.class.options(path, options) do
174182
header 'Allow', allow_header
175183
status 204
176184
''
@@ -179,15 +187,13 @@ def generate_options_method(path, allow_header)
179187

180188
# Generate a route that returns an HTTP 405 response for a user defined
181189
# path on methods not specified
182-
def generate_not_allowed_method(path, allowed_methods, allow_header)
190+
def generate_not_allowed_method(path, allowed_methods, allow_header, endpoint = nil)
183191
not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - allowed_methods
184192
not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
185193

186194
return if not_allowed_methods.empty?
187195

188-
self.class.route(not_allowed_methods, path) do
189-
fail Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allow_header)
190-
end
196+
@router.associate_routes(path, not_allowed_methods, allow_header, endpoint)
191197
end
192198

193199
# Allows definition of endpoints that ignore the versioning configuration

lib/grape/dsl/inside_route.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,10 @@ def present(*args)
269269
#
270270
# desc "Returns the route description."
271271
# get '/' do
272-
# route.route_description
272+
# route.description
273273
# end
274274
def route
275-
env[Grape::Env::RACK_ROUTING_ARGS][:route_info]
275+
env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info]
276276
end
277277

278278
# Attempt to locate the Entity class for a given object, if not given

lib/grape/dsl/routing.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def mount(mounts)
8282
in_setting = inheritable_setting
8383

8484
if app.respond_to?(:inheritable_setting, true)
85-
mount_path = Rack::Mount::Utils.normalize_path(path)
85+
mount_path = Grape::Router.normalize_path(path)
8686
app.top_level_setting.namespace_stackable[:mount_path] = mount_path
8787

8888
app.inherit_settings(inheritable_setting)
@@ -98,6 +98,7 @@ def mount(mounts)
9898
method: :any,
9999
path: path,
100100
app: app,
101+
forward_match: !app.respond_to?(:inheritable_setting),
101102
for: self
102103
)
103104
end

0 commit comments

Comments
 (0)