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
Attributes
Public Class Methods
# File lib/grape/api.rb, line 383 def after(&block) imbue(:afters, [block]) end
# File lib/grape/api.rb, line 379 def after_validation(&block) imbue(:after_validations, [block]) end
Add an authentication type to the API
. Currently only `:http_basic`, `:http_digest` and `:oauth2` are supported.
# File lib/grape/api.rb, line 306 def auth(type = nil, options = {}, &block) if type set(:auth, { type: type.to_sym, proc: block }.merge(options)) else settings[:auth] end end
# File lib/grape/api.rb, line 371 def before(&block) imbue(:befores, [block]) end
# File lib/grape/api.rb, line 375 def before_validation(&block) imbue(:before_validations, [block]) end
# File lib/grape/api.rb, line 38 def call(env) LOCK.synchronize { compile } unless instance call!(env) end
# File lib/grape/api.rb, line 43 def call!(env) instance.call(env) end
# File lib/grape/api.rb, line 483 def cascade(value = nil) if value.nil? settings.key?(:cascade) ? !!settings[:cascade] : true else set(:cascade, value) end end
# File lib/grape/api.rb, line 34 def change! @instance = nil end
# File lib/grape/api.rb, line 30 def compile @instance ||= new end
Specify additional content-types, e.g.:
content_type :xls, 'application/vnd.ms-excel'
# File lib/grape/api.rb, line 173 def content_type(key, val) settings.imbue(:content_types, key.to_sym => val) end
All available content types.
# File lib/grape/api.rb, line 178 def content_types Grape::ContentTypes.content_types_for(settings[:content_types]) end
Specify a default error formatter.
# File lib/grape/api.rb, line 152 def default_error_formatter(new_formatter_name = nil) if new_formatter_name new_formatter = Grape::ErrorFormatter::Base.formatter_for(new_formatter_name, {}) set(:default_error_formatter, new_formatter) else settings[:default_error_formatter] end end
Specify the default status code for errors.
# File lib/grape/api.rb, line 183 def default_error_status(new_status = nil) new_status ? set(:default_error_status, new_status) : settings[:default_error_status] end
Specify the default format for the API's serializers. May be `:json` or `:txt` (default).
# File lib/grape/api.rb, line 121 def default_format(new_format = nil) new_format ? set(:default_format, new_format.to_sym) : settings[:default_format] end
# File lib/grape/api.rb, line 403 def delete(paths = ['/'], options = {}, &block) route('DELETE', paths, options, &block) end
Add a description to the next namespace or function.
# File lib/grape/api.rb, line 115 def desc(description, options = {}) @last_description = options.merge(description: description) end
Do not route HEAD requests to GET requests automatically
# File lib/grape/api.rb, line 70 def do_not_route_head! set(:do_not_route_head, true) end
Do not automatically route OPTIONS
# File lib/grape/api.rb, line 75 def do_not_route_options! set(:do_not_route_options, true) end
# File lib/grape/api.rb, line 161 def error_formatter(format, options) if options.is_a?(Hash) && options.key?(:with) formatter = options[:with] else formatter = options end settings.imbue(:error_formatters, format.to_sym => formatter) end
Specify the format for the API's serializers. May be `:json`, `:xml`, `:txt`, etc.
# File lib/grape/api.rb, line 127 def format(new_format = nil) if new_format set(:format, new_format.to_sym) # define the default error formatters set(:default_error_formatter, Grape::ErrorFormatter::Base.formatter_for(new_format, {})) # define a single mime type mime_type = content_types[new_format.to_sym] raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type settings.imbue(:content_types, new_format.to_sym => mime_type) else settings[:format] end end
Specify a custom formatter for a content-type.
# File lib/grape/api.rb, line 142 def formatter(content_type, new_formatter) settings.imbue(:formatters, content_type.to_sym => new_formatter) end
# File lib/grape/api.rb, line 387 def get(paths = ['/'], options = {}, &block) route('GET', paths, options, &block) end
# File lib/grape/api.rb, line 399 def head(paths = ['/'], options = {}, &block) route('HEAD', paths, options, &block) end
Add helper methods that will be accessible from any endpoint within this namespace (and child namespaces).
When called without a block, all known helpers within this scope are included.
@param [Module] new_mod optional module of methods to include @param [Block] block optional block of methods to include
@example Define some helpers.
class ExampleAPI < Grape::API helpers do def current_user User.find_by_id(params[:token]) end end end
# File lib/grape/api.rb, line 279 def helpers(new_mod = nil, &block) if block_given? || new_mod mod = settings.peek[:helpers] || Module.new if new_mod inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(Helpers) mod.class_eval do include new_mod end end if block_given? inject_api_helpers_to_mod(mod) do mod.class_eval(&block) end end set(:helpers, mod) else mod = Module.new settings.stack.each do |s| mod.send :include, s[:helpers] if s[:helpers] end change! mod end end
Add HTTP Basic authorization to the API
.
@param [Hash] options A hash of options. @option options [String] :realm “API Authorization” The HTTP Basic realm.
# File lib/grape/api.rb, line 318 def http_basic(options = {}, &block) options[:realm] ||= "API Authorization" auth :http_basic, options, &block end
# File lib/grape/api.rb, line 323 def http_digest(options = {}, &block) options[:realm] ||= "API Authorization" options[:opaque] ||= "secret" auth :http_digest, options, &block end
Add to a configuration value for this namespace.
@param key [Symbol] The key of the configuration variable. @param value [Object] The value to which to set the configuration variable.
# File lib/grape/api.rb, line 60 def imbue(key, value) settings.imbue(key, value) end
# File lib/grape/api.rb, line 14 def logger(logger = nil) if logger @logger = logger else @logger ||= Logger.new($stdout) end end
Retrieve an array of the middleware classes and arguments that are currently applied to the application.
# File lib/grape/api.rb, line 467 def middleware settings.stack.inject([]) do |a, s| a += s[:middleware] if s[:middleware] a end end
# File lib/grape/api.rb, line 329 def mount(mounts) mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair) mounts.each_pair do |app, path| if app.respond_to?(:inherit_settings, true) app_settings = settings.clone mount_path = Rack::Mount::Utils.normalize_path([settings[:mount_path], path].compact.join("/")) app_settings.set :mount_path, mount_path app.inherit_settings(app_settings) end endpoints << Grape::Endpoint.new( settings.clone, method: :any, path: path, app: app ) end end
# File lib/grape/api.rb, line 415 def namespace(space = nil, options = {}, &block) if space || block_given? previous_namespace_description = @namespace_description @namespace_description = (@namespace_description || {}).deep_merge(@last_description || {}) @last_description = nil nest(block) do set(:namespace, Namespace.new(space, options)) if space end @namespace_description = previous_namespace_description else Namespace.joined_space_path(settings) end end
# File lib/grape/api.rb, line 537 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
# File lib/grape/api.rb, line 407 def options(paths = ['/'], options = {}, &block) route('OPTIONS', paths, options, &block) end
Specify a custom parser for a content-type.
# File lib/grape/api.rb, line 147 def parser(content_type, new_parser) settings.imbue(:parsers, content_type.to_sym => new_parser) end
# File lib/grape/api.rb, line 411 def patch(paths = ['/'], options = {}, &block) route('PATCH', paths, options, &block) end
# File lib/grape/api.rb, line 391 def post(paths = ['/'], options = {}, &block) route('POST', paths, options, &block) end
Define a root URL prefix for your entire API
.
# File lib/grape/api.rb, line 65 def prefix(prefix = nil) prefix ? set(:root_prefix, prefix) : settings[:root_prefix] end
# File lib/grape/api.rb, line 395 def put(paths = ['/'], options = {}, &block) route('PUT', paths, options, &block) end
Allows you to specify a default representation entity for a class. This allows you to map your models to their respective entities once and then simply call `present` with the model.
@example
class ExampleAPI < Grape::API represent User, with: Entity::User get '/me' do present current_user # with: Entity::User is assumed end end
Note that Grape
will automatically go up the class ancestry to try to find a representing entity, so if you, for example, define an entity to represent `Object` then all presented objects will bubble up and utilize the entity provided on that `represent` call.
@param model_class [Class] The model class that will be represented. @option options [Class] :with The entity class that will represent the model.
# File lib/grape/api.rb, line 255 def represent(model_class, options) raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with] && options[:with].is_a?(Class) imbue(:representations, model_class => options[:with]) end
Allows you to rescue certain exceptions that occur to return a grape error rather than raising all the way to the server level.
@example Rescue from custom exceptions
class ExampleAPI < Grape::API class CustomError < StandardError; end rescue_from CustomError end
@overload rescue_from
(*exception_classes, options = {})
@param [Array] exception_classes A list of classes that you want to rescue, or the symbol :all to rescue from all exceptions. @param [Block] block Execution block to handle the given exception. @param [Hash] options Options for the rescue usage. @option options [Boolean] :backtrace Include a backtrace in the rescue response. @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes @param [Proc] handler Execution proc to handle the given exception as an alternative to passing a block
# File lib/grape/api.rb, line 207 def rescue_from(*args, &block) if args.last.is_a?(Proc) handler = args.pop elsif block_given? handler = block end options = args.last.is_a?(Hash) ? args.pop : {} handler ||= proc { options[:with] } if options.key?(:with) if args.include?(:all) set(:rescue_all, true) imbue :all_rescue_handler, handler else handler_type = case options[:rescue_subclasses] when nil, true :rescue_handlers else :base_only_rescue_handlers end imbue handler_type, Hash[args.map { |arg| [arg, handler] }] end imbue(:rescue_options, options) end
# File lib/grape/api.rb, line 22 def reset! @settings = Grape::Util::HashStack.new @route_set = Rack::Mount::RouteSet.new @endpoints = [] @routes = nil reset_validations! end
Defines a route that will be recognized by the Grape
API
.
@param methods [HTTP Verb] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted. @param paths [String] One or more strings representing the URL segment(s) for this route.
@example Defining a basic route.
class MyAPI < Grape::API route(:any, '/hello') do {hello: 'world'} end end
# File lib/grape/api.rb, line 359 def route(methods, paths = ['/'], route_options = {}, &block) endpoint_options = { method: methods, path: paths, route_options: (@namespace_description || {}).deep_merge(@last_description || {}).deep_merge(route_options || {}) } endpoints << Grape::Endpoint.new(settings.clone, endpoint_options, &block) @last_description = nil reset_validations! end
Thie method allows you to quickly define a parameter route segment in your API
.
@param param [Symbol] The name of the parameter you wish to declare. @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
# File lib/grape/api.rb, line 434 def route_param(param, options = {}, &block) options = options.dup options[:requirements] = { param.to_sym => options[:requirements] } if options[:requirements].is_a?(Regexp) namespace(":#{param}", options, &block) end
Create a scope without affecting the URL.
@param name [Symbol] Purely placebo, just allows to to name the scope to make the code more readable.
# File lib/grape/api.rb, line 448 def scope(name = nil, &block) nest(block) end
Set a configuration value for this namespace.
@param key [Symbol] The key of the configuration variable. @param value [Object] The value to which to set the configuration variable.
# File lib/grape/api.rb, line 51 def set(key, value) settings[key.to_sym] = value end
Apply a custom middleware to the API
. Applies to the current namespace and any children, but not parents.
@param middleware_class [Class] The class of the middleware you'd like
to inject.
# File lib/grape/api.rb, line 458 def use(middleware_class, *args, &block) arr = [middleware_class, *args] arr << block if block_given? imbue(:middleware, [arr]) end
Specify an API
version.
@example API
with legacy support.
class MyAPI < Grape::API version 'v2' get '/main' do {some: 'data'} end version 'v1' do get '/main' do {legacy: 'data'} end end end
# File lib/grape/api.rb, line 96 def version(*args, &block) if args.any? options = args.pop if args.last.is_a? Hash options ||= {} options = { using: :path }.merge(options) raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor) @versions = versions | args nest(block) do set(:version, args) set(:version_options, options) end end @versions.last unless @versions.nil? end
Protected Class Methods
# File lib/grape/api.rb, line 522 def inherit_settings(other_stack) settings.prepend other_stack endpoints.each do |e| e.settings.prepend(other_stack) e.options[:app].inherit_settings(other_stack) if e.options[:app].respond_to?(:inherit_settings, true) end end
# File lib/grape/api.rb, line 517 def inherited(subclass) subclass.reset! subclass.logger = logger.clone end
# File lib/grape/api.rb, line 530 def inject_api_helpers_to_mod(mod, &block) mod.extend(Helpers) yield if block_given? mod.api_changed(self) end
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 504 def nest(*blocks, &block) blocks.reject! { |b| b.nil? } if blocks.any? settings.push # create a new context to eval the follow instance_eval(&block) if block_given? blocks.each { |b| instance_eval(&b) } settings.pop # when finished, we pop the context reset_validations! else instance_eval(&block) end end
# File lib/grape/api.rb, line 493 def prepare_routes routes = [] endpoints.each do |endpoint| routes.concat(endpoint.routes) end routes end
Public Instance Methods
# File lib/grape/api.rb, line 546 def call(env) status, headers, body = @route_set.call(env) headers.delete('X-Cascade') unless cascade? [status, headers, body] end
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 560 def cascade? return !!self.class.settings[:cascade] if self.class.settings.key?(:cascade) return !!self.class.settings[:version_options][:cascade] if self.class.settings[:version_options] && self.class.settings[:version_options].key?(:cascade) true end
Private Instance Methods
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 574 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| methods_per_path[route.route_path] ||= [] methods_per_path[route.route_path] << route.route_method 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_versioning do methods_per_path.each do |path, methods| allowed_methods = methods.dup unless self.class.settings[:do_not_route_head] allowed_methods |= ['HEAD'] if allowed_methods.include?('GET') end allow_header = (['OPTIONS'] | allowed_methods).join(', ') unless self.class.settings[:do_not_route_options] unless allowed_methods.include?('OPTIONS') self.class.options(path, {}) do header 'Allow', allow_header status 204 '' end end end not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - allowed_methods not_allowed_methods << 'OPTIONS' if self.class.settings[:do_not_route_options] self.class.route(not_allowed_methods, path) do header 'Allow', allow_header status 405 '' end end end end
# File lib/grape/api.rb, line 617 def without_versioning(&block) self.class.settings.push(version: nil, version_options: nil) yield self.class.settings.pop end