class Grape::API::Instance
from this will represent a different API instance
The API Instance class, is the engine behind Grape::API. Each class that inherits
def add_head_not_allowed_methods_and_options_methods
will return an HTTP 405 response for any HTTP method that the resource
with a list of HTTP methods that can be called. Also add a route that
For every resource add a 'OPTIONS' route that returns an HTTP 204 response
def add_head_not_allowed_methods_and_options_methods # The paths we collected are prepared (cf. Path#prepare), so they # contain already versioning information when using path versioning. all_routes = self.class.endpoints.flat_map(&:routes) # Read the settings the helper routes need from a *copy* with the # root-prefix/versioning keys stripped, so adding these routes won't # prepend versioning information again. This used to delete those keys # from the shared class-level settings and restore them in an ensure; # a request served concurrently on another instance during a runtime # recompile could observe the missing keys (e.g. via #cascade?). A # local copy keeps that mutation off the shared object. namespace_inheritable = self.class.inheritable_setting.namespace_inheritable.to_hash.except(*ROOT_PREFIX_VERSIONING_KEYS) collect_route_config_per_pattern(all_routes, namespace_inheritable) end
def base=(grape_api)
def base=(grape_api) @base = grape_api grape_api.instances << self end
def base_instance?
def base_instance? self == @base.base_instance end
def call(env)
the headers, and the body. See [the rack specification]
from Rack and ultimately returns an array of three values: the status,
This is the interface point between Rack and Grape; it accepts a request
def call(env) compile! @instance.call(env) end
def call(env)
def call(env) status, headers, response = @router.call(env) unless cascade? headers = Grape::Util::Header.new.merge(headers) headers.delete('X-Cascade') end [status, headers, response] end
def cascade?
errors from reaching upstream. This is effectivelly done by unsetting
In some applications (e.g. mounting grape on rails), one might need to trap
looking for a matching route on other resources.
and sets it to 'pass', indicating to grape's parents they should keep
route. In this case, Grape::Router adds a X-Cascade header to the response
Some requests may return a HTTP 404 error if grape cannot find a matching
def cascade? namespace_inheritable = self.class.inheritable_setting.namespace_inheritable return namespace_inheritable[:cascade] if namespace_inheritable.key?(:cascade) return namespace_inheritable[:version_options].cascade if namespace_inheritable[:version_options] true end
def change!
def change! @instance = nil end
def collect_route_config_per_pattern(all_routes, namespace_inheritable)
def collect_route_config_per_pattern(all_routes, namespace_inheritable) routes_by_regexp = all_routes.group_by(&:pattern_regexp) # Build the configuration based on the first endpoint and the collection of methods supported. routes_by_regexp.each_value do |routes| next if routes.any? { |route| route.request_method == '*' } last_route = routes.last # Most of the configuration is taken from the last endpoint allowed_methods = routes.map(&:request_method) allowed_methods |= [Rack::HEAD] if !namespace_inheritable[:do_not_route_head] && allowed_methods.include?(Rack::GET) allow_header = namespace_inheritable[:do_not_route_options] ? allowed_methods : [Rack::OPTIONS] | allowed_methods last_route.app.options_route_enabled = true unless namespace_inheritable[:do_not_route_options] || allowed_methods.include?(Rack::OPTIONS) greedy_route = Grape::Router::GreedyRoute.new(last_route.pattern, endpoint: last_route.app, allow_header:) @router.associate_routes(greedy_route) end end
def compile!
def compile! return if @instance LOCK.synchronize { @instance ||= new } end
def inherit_settings(other_settings)
def inherit_settings(other_settings) top_level_setting.inherit_from other_settings.point_in_time_copy # Propagate any inherited params down to our endpoints, and reset any # compiled routes. endpoints.each do |e| e.inherit_settings(top_level_setting.namespace_stackable) e.reset_routes! end reset_routes! end
def inherited(subclass)
def inherited(subclass) super subclass.reset! subclass.logger logger.clone end
def initialize
Builds the routes from the defined endpoints, effectively compiling
def initialize @router = Router.new add_head_not_allowed_methods_and_options_methods self.class.endpoints.each do |endpoint| endpoint.mount_in(@router) end @router.compile! @router.freeze end
def recognize_path(path)
def recognize_path(path) compile! @instance.router.recognize_path(path) end
def reset!
def reset! reset_endpoints! reset_routes! reset_validations! end