1+ require 'grape/router'
2+
13module 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
0 commit comments