class Grape::API::Instance

The API Instance class, is the engine behind Grape::API. Each class that inherits from this will represent a different API instance

Constants

Boolean
LOCK

A class-level lock to ensure the API is not compiled by multiple threads simultaneously within the same process.

Attributes

base[R]
configuration[RW]
instance[R]
router[R]

Public Class Methods

base=(grape_api) click to toggle source
# File lib/grape/api/instance.rb, line 22
def base=(grape_api)
  @base = grape_api
  grape_api.instances << self
end
base_instance?() click to toggle source
# File lib/grape/api/instance.rb, line 31
def base_instance?
  self == base.base_instance
end
call(env) click to toggle source

This is the interface point between Rack and Grape; it accepts a request from Rack and ultimately returns an array of three values: the status, the headers, and the body. See [the rack specification] (www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.

# File lib/grape/api/instance.rb, line 61
def call(env)
  compile!
  call!(env)
end
call!(env) click to toggle source

A non-synchronized version of ::call.

# File lib/grape/api/instance.rb, line 67
def call!(env)
  instance.call(env)
end
cascade(value = nil) click to toggle source

(see cascade?)

# File lib/grape/api/instance.rb, line 72
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
change!() click to toggle source

Wipe the compiled API so we can recompile after changes were made.

# File lib/grape/api/instance.rb, line 53
def change!
  @instance = nil
end
compile() click to toggle source

Parses the API’s definition and compiles it into an instance of Grape::API.

# File lib/grape/api/instance.rb, line 48
def compile
  @instance ||= new # rubocop:disable Naming/MemoizedInstanceVariableName
end
compile!() click to toggle source
# File lib/grape/api/instance.rb, line 80
def compile!
  return if instance

  LOCK.synchronize { compile unless instance }
end
given(conditional_option, &block) click to toggle source
# File lib/grape/api/instance.rb, line 14
def given(conditional_option, &block)
  evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block
end
mounted(&block) click to toggle source
# File lib/grape/api/instance.rb, line 18
def mounted(&block)
  evaluate_as_instance_with_configuration(block, lazy: true)
end
new() click to toggle source

Builds the routes from the defined endpoints, effectively compiling this API into a usable form.

# File lib/grape/api/instance.rb, line 151
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
recognize_path(path) click to toggle source

see Grape::Router#recognize_path

# File lib/grape/api/instance.rb, line 87
def recognize_path(path)
  compile!
  instance.router.recognize_path(path)
end
reset!() click to toggle source

Clears all defined routes, endpoints, etc., on this API.

# File lib/grape/api/instance.rb, line 40
def reset!
  reset_endpoints!
  reset_routes!
  reset_validations!
end
to_s() click to toggle source
Calls superclass method
# File lib/grape/api/instance.rb, line 27
def to_s
  base&.to_s || super
end

Protected Class Methods

evaluate_as_instance_with_configuration(block, lazy: false) click to toggle source
# File lib/grape/api/instance.rb, line 112
def evaluate_as_instance_with_configuration(block, lazy: false)
  lazy_block = Grape::Util::Lazy::Block.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
inherit_settings(other_settings) click to toggle source
# File lib/grape/api/instance.rb, line 133
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
inherited(subclass) click to toggle source
Calls superclass method
# File lib/grape/api/instance.rb, line 127
def inherited(subclass)
  super
  subclass.reset!
  subclass.logger = logger.clone
end
nest(*blocks, &block) click to toggle source

Execute first the provided block, then each of the block passed in. Allows for simple ‘before’ setups of settings stack pushes.

# File lib/grape/api/instance.rb, line 101
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
prepare_routes() click to toggle source
# File lib/grape/api/instance.rb, line 94
def prepare_routes
  endpoints.map(&:routes).flatten
end

Public Instance Methods

call(env) click to toggle source

Handle a request. See Rack documentation for what ‘env` is.

# File lib/grape/api/instance.rb, line 163
def call(env)
  status, headers, response = @router.call(env)
  unless cascade?
    headers = Grape::Util::Header.new.merge(headers)
    headers.delete(Grape::Http::Headers::X_CASCADE)
  end

  [status, headers, response]
end
cascade?() click to toggle source

Some requests may return a HTTP 404 error if grape cannot find a matching route. In this case, Grape::Router adds a X-Cascade header to the response and sets it to ‘pass’, indicating to grape’s parents they should keep looking for a matching route on other resources.

In some applications (e.g. mounting grape on rails), one might need to trap errors from reaching upstream. This is effectivelly done by unsetting X-Cascade. Default :cascade is true.

# File lib/grape/api/instance.rb, line 181
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

Private Instance Methods

add_head_not_allowed_methods_and_options_methods() click to toggle source

For every resource add a ‘OPTIONS’ route that returns an HTTP 204 response with a list of HTTP methods that can be called. Also add a route that will return an HTTP 405 response for any HTTP method that the resource cannot handle.

# File lib/grape/api/instance.rb, line 196
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 |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)

        allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods)

        config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)

        attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
        generate_not_allowed_method(config[:pattern], **attributes)
      end
    end
  end
end
collect_route_config_per_pattern() click to toggle source
# File lib/grape/api/instance.rb, line 222
def collect_route_config_per_pattern
  all_routes       = self.class.endpoints.map(&:routes).flatten
  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.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
generate_not_allowed_method(pattern, allowed_methods: [], **attributes) click to toggle source

Generate a route that returns an HTTP 405 response for a user defined path on methods not specified

# File lib/grape/api/instance.rb, line 243
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
without_root_prefix() { || ... } click to toggle source

Allows definition of endpoints that ignore the root prefix used by the rest of your API.

# File lib/grape/api/instance.rb, line 271
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
without_versioning() { || ... } click to toggle source

Allows definition of endpoints that ignore the versioning configuration used by the rest of your API.

# File lib/grape/api/instance.rb, line 256
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