class Grape::API

The API class is the primary entry point for creating Grape APIs. Users should subclass this class in order to build an API.

Constants

Boolean
LOCK

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

Attributes

instance[R]

Public Class Methods

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.rb, line 36
def call(env)
  LOCK.synchronize { compile } unless instance
  call!(env)
end
call!(env) click to toggle source

A non-synchronized version of ::call.

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

(see cascade?)

# File lib/grape/api.rb, line 47
def cascade(value = nil)
  if value.nil?
    inheritable_setting.namespace_inheritable.keys.include?(: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.rb, line 28
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.rb, line 23
def compile
  @instance ||= new
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.rb, line 89
def initialize
  @route_set = Rack::Mount::RouteSet.new
  add_head_not_allowed_methods_and_options_methods
  self.class.endpoints.each do |endpoint|
    endpoint.mount_in(@route_set)
  end

  @route_set.freeze
end
reset!() click to toggle source

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

# File lib/grape/api.rb, line 15
def reset!
  reset_endpoints!
  reset_routes!
  reset_validations!
end

Protected Class Methods

inherit_settings(other_settings) click to toggle source
# File lib/grape/api.rb, line 80
def inherit_settings(other_settings)
  top_level_setting.inherit_from other_settings.point_in_time_copy

  reset_routes!
end
inherited(subclass) click to toggle source
# File lib/grape/api.rb, line 75
def inherited(subclass)
  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.rb, line 64
def nest(*blocks, &block)
  blocks.reject!(&:nil?)
  if blocks.any?
    instance_eval(&block) if block_given?
    blocks.each { |b| instance_eval(&b) }
    reset_validations!
  else
    instance_eval(&block)
  end
end
prepare_routes() click to toggle source
# File lib/grape/api.rb, line 57
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.rb, line 100
def call(env)
  result = @route_set.call(env)
  result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
  result
end
cascade?() click to toggle source

Some requests may return a HTTP 404 error if grape cannot find a matching route. In this case, Rack::Mount 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.rb, line 114
def cascade?
  return !!self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
  return !!self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && 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.rb, line 128
def add_head_not_allowed_methods_and_options_methods
  methods_per_path = {}

  self.class.endpoints.each do |endpoint|
    routes = endpoint.routes
    routes.each do |route|
      route_path = route.route_path
                   .gsub(/\(.*\)/, '') # ignore any optional portions
                   .gsub(%r{\:[^\/.?]+}, ':x') # substitute variable names to avoid conflicts

      methods_per_path[route_path] ||= []
      methods_per_path[route_path] << route.route_method

      # using the :any shorthand produces [nil] for route methods, substitute all manually
      methods_per_path[route_path] = %w(GET PUT POST DELETE PATCH HEAD OPTIONS) if methods_per_path[route_path].compact.empty?
    end
  end

  # 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
      methods_per_path.each do |path, methods|
        allowed_methods = methods.dup

        unless self.class.namespace_inheritable(:do_not_route_head)
          allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
        end

        allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')

        unless self.class.namespace_inheritable(:do_not_route_options)
          generate_options_method(path, allow_header) unless allowed_methods.include?(Grape::Http::Headers::OPTIONS)
        end

        generate_not_allowed_method(path, allowed_methods, allow_header)
      end
    end
  end
end
generate_not_allowed_method(path, allowed_methods, allow_header) 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.rb, line 182
def generate_not_allowed_method(path, allowed_methods, allow_header)
  not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - allowed_methods
  not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)

  return if not_allowed_methods.empty?

  self.class.route(not_allowed_methods, path) do
    fail Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allow_header)
  end
end
generate_options_method(path, allow_header) click to toggle source

Generate an 'OPTIONS' route for a pre-exisiting user defined route

# File lib/grape/api.rb, line 172
def generate_options_method(path, allow_header)
  self.class.options(path, {}) do
    header 'Allow', allow_header
    status 204
    ''
  end
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.rb, line 210
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.rb, line 195
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