class Grape::API::Instance
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 versioned_route_configs = collect_route_config_per_pattern # The paths we collected are prepared (cf. Path#prepare), so they # contain already versioning information when using path versioning. # Disable versioning so adding a route won't prepend versioning # informations again. without_root_prefix do without_versioning do versioned_route_configs.each do |config| next if config[:options][:matching_wildchar] allowed_methods = config[:methods].dup allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET) allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods) config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS) attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header) generate_not_allowed_method(config[:pattern], **attributes) end end end 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! call!(env) end
def call(env)
def call(env) result = @router.call(env) result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade? result end
def call!(env)
def call!(env) instance.call(env) end
def cascade(value = nil)
def cascade(value = nil) if value.nil? inheritable_setting.namespace_inheritable.key?(:cascade) ? !namespace_inheritable(:cascade).nil? : true else namespace_inheritable(:cascade, value) end 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? return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.key?(:cascade) return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options)&.key?(:cascade) true end
def change!
def change! @instance = nil end
def collect_route_config_per_pattern
def collect_route_config_per_pattern all_routes = self.class.endpoints.map(&:routes).flatten routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp } # Build the configuration based on the first endpoint and the collection of methods supported. routes_by_regexp.values.map do |routes| last_route = routes.last # Most of the configuration is taken from the last endpoint matching_wildchar = routes.any? { |route| route.request_method == '*' } { options: { matching_wildchar: matching_wildchar }, pattern: last_route.pattern, requirements: last_route.requirements, path: last_route.origin, endpoint: last_route.app, methods: matching_wildchar ? Grape::Http::Headers::SUPPORTED_METHODS : routes.map(&:request_method) } end end
def compile
Parses the API's definition and compiles it into an instance of
def compile @instance ||= new end
def compile!
def compile! return if instance LOCK.synchronize { compile unless instance } end
def evaluate_as_instance_with_configuration(block, lazy: false)
def evaluate_as_instance_with_configuration(block, lazy: false) lazy_block = Grape::Util::LazyBlock.new do |configuration| value_for_configuration = configuration self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy? response = instance_eval(&block) self.configuration = value_for_configuration response end if base && base_instance? && lazy lazy_block else lazy_block.evaluate_from(configuration) end end
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
Generate a route that returns an HTTP 405 response for a user defined
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes) supported_methods = if self.class.namespace_inheritable(:do_not_route_options) Grape::Http::Headers::SUPPORTED_METHODS else Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS end not_allowed_methods = supported_methods - allowed_methods @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes) end
def given(conditional_option, &block)
def given(conditional_option, &block) evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block 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) 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 mounted(&block)
def mounted(&block) evaluate_as_instance_with_configuration(block, lazy: true) end
def nest(*blocks, &block)
block passed in. Allows for simple 'before' setups
Execute first the provided block, then each of the
def nest(*blocks, &block) blocks.compact! if blocks.any? evaluate_as_instance_with_configuration(block) if block blocks.each { |b| evaluate_as_instance_with_configuration(b) } reset_validations! else instance_eval(&block) end end
def prepare_routes
def prepare_routes endpoints.map(&:routes).flatten 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
def to_s
def to_s base&.to_s || super end
def without_root_prefix(&_block)
Allows definition of endpoints that ignore the root prefix used by the
def without_root_prefix(&_block) old_prefix = self.class.namespace_inheritable(:root_prefix) self.class.namespace_inheritable_to_nil(:root_prefix) yield self.class.namespace_inheritable(:root_prefix, old_prefix) end
def without_versioning(&_block)
Allows definition of endpoints that ignore the versioning configuration
def without_versioning(&_block) old_version = self.class.namespace_inheritable(:version) old_version_options = self.class.namespace_inheritable(:version_options) self.class.namespace_inheritable_to_nil(:version) self.class.namespace_inheritable_to_nil(:version_options) yield self.class.namespace_inheritable(:version, old_version) self.class.namespace_inheritable(:version_options, old_version_options) end