lib/grape/api.rb in grape-0.8.0 vs lib/grape/api.rb in grape-0.9.0
- old
+ new
@@ -1,26 +1,25 @@
module Grape
# The API class is the primary entry point for
# creating Grape APIs.Users should subclass this
# class in order to build an API.
class API
- extend Validations::ClassMethods
+ extend Grape::Middleware::Auth::DSL
+ include Grape::DSL::Validations
+ include Grape::DSL::Callbacks
+ include Grape::DSL::Configuration
+ include Grape::DSL::Helpers
+ include Grape::DSL::Middleware
+ include Grape::DSL::RequestResponse
+ include Grape::DSL::Routing
+
class << self
- attr_reader :endpoints, :instance, :routes, :route_set, :settings, :versions
- attr_writer :logger
+ attr_reader :instance
LOCK = Mutex.new
- def logger(logger = nil)
- if logger
- @logger = logger
- else
- @logger ||= Logger.new($stdout)
- end
- end
-
def reset!
@settings = Grape::Util::HashStack.new
@route_set = Rack::Mount::RouteSet.new
@endpoints = []
@routes = nil
@@ -42,10 +41,25 @@
def call!(env)
instance.call(env)
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.
+ def scope(name = nil, &block)
+ nest(block)
+ end
+
+ def cascade(value = nil)
+ if value.nil?
+ settings.key?(:cascade) ? !!settings[:cascade] : true
+ else
+ set(:cascade, value)
+ end
+ 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.
def set(key, value)
@@ -59,437 +73,10 @@
# @param value [Object] The value to which to set the configuration variable.
def imbue(key, value)
settings.imbue(key, value)
end
- # Define a root URL prefix for your entire API.
- def prefix(prefix = nil)
- prefix ? set(:root_prefix, prefix) : settings[:root_prefix]
- end
-
- # Do not route HEAD requests to GET requests automatically
- def do_not_route_head!
- set(:do_not_route_head, true)
- end
-
- # Do not automatically route OPTIONS
- def do_not_route_options!
- set(:do_not_route_options, true)
- 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
- #
- 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
-
- # Add a description to the next namespace or function.
- def desc(description, options = {})
- @last_description = options.merge(description: description)
- end
-
- # Specify the default format for the API's serializers.
- # May be `:json` or `:txt` (default).
- def default_format(new_format = nil)
- new_format ? set(:default_format, new_format.to_sym) : settings[:default_format]
- end
-
- # Specify the format for the API's serializers.
- # May be `:json`, `:xml`, `:txt`, etc.
- 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.
- def formatter(content_type, new_formatter)
- settings.imbue(:formatters, content_type.to_sym => new_formatter)
- end
-
- # Specify a custom parser for a content-type.
- def parser(content_type, new_parser)
- settings.imbue(:parsers, content_type.to_sym => new_parser)
- end
-
- # Specify a default error formatter.
- 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
-
- 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 additional content-types, e.g.:
- # content_type :xls, 'application/vnd.ms-excel'
- def content_type(key, val)
- settings.imbue(:content_types, key.to_sym => val)
- end
-
- # All available content types.
- def content_types
- Grape::ContentTypes.content_types_for(settings[:content_types])
- end
-
- # Specify the default status code for errors.
- def default_error_status(new_status = nil)
- new_status ? set(:default_error_status, new_status) : settings[:default_error_status]
- 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
- 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
-
- # 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.
- 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
-
- # 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
- #
- 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 an authentication type to the API. Currently
- # only `:http_basic`, `:http_digest` and `:oauth2` are supported.
- def auth(type = nil, options = {}, &block)
- if type
- set(:auth, { type: type.to_sym, proc: block }.merge(options))
- else
- settings[:auth]
- 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.
- def http_basic(options = {}, &block)
- options[:realm] ||= "API Authorization"
- auth :http_basic, options, &block
- end
-
- def http_digest(options = {}, &block)
- options[:realm] ||= "API Authorization"
- options[:opaque] ||= "secret"
- auth :http_digest, options, &block
- end
-
- 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
-
- # 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
- 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
-
- def before(&block)
- imbue(:befores, [block])
- end
-
- def before_validation(&block)
- imbue(:before_validations, [block])
- end
-
- def after_validation(&block)
- imbue(:after_validations, [block])
- end
-
- def after(&block)
- imbue(:afters, [block])
- end
-
- def get(paths = ['/'], options = {}, &block)
- route('GET', paths, options, &block)
- end
-
- def post(paths = ['/'], options = {}, &block)
- route('POST', paths, options, &block)
- end
-
- def put(paths = ['/'], options = {}, &block)
- route('PUT', paths, options, &block)
- end
-
- def head(paths = ['/'], options = {}, &block)
- route('HEAD', paths, options, &block)
- end
-
- def delete(paths = ['/'], options = {}, &block)
- route('DELETE', paths, options, &block)
- end
-
- def options(paths = ['/'], options = {}, &block)
- route('OPTIONS', paths, options, &block)
- end
-
- def patch(paths = ['/'], options = {}, &block)
- route('PATCH', paths, options, &block)
- end
-
- 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
-
- # 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.
- 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
-
- alias_method :group, :namespace
- alias_method :resource, :namespace
- alias_method :resources, :namespace
- alias_method :segment, :namespace
-
- # 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.
- def scope(name = nil, &block)
- nest(block)
- 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.
- def use(middleware_class, *args, &block)
- arr = [middleware_class, *args]
- arr << block if block_given?
- imbue(:middleware, [arr])
- end
-
- # Retrieve an array of the middleware classes
- # and arguments that are currently applied to the
- # application.
- def middleware
- settings.stack.inject([]) do |a, s|
- a += s[:middleware] if s[:middleware]
- a
- end
- end
-
- # An array of API routes.
- def routes
- @routes ||= prepare_routes
- end
-
- def versions
- @versions ||= []
- end
-
- def cascade(value = nil)
- if value.nil?
- settings.key?(:cascade) ? !!settings[:cascade] : true
- else
- set(:cascade, value)
- end
- end
-
protected
def prepare_routes
routes = []
endpoints.each do |endpoint|
@@ -524,16 +111,10 @@
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
-
- def inject_api_helpers_to_mod(mod, &block)
- mod.extend(Helpers)
- yield if block_given?
- mod.api_changed(self)
- end
end
def initialize
@route_set = Rack::Mount::RouteSet.new
add_head_not_allowed_methods_and_options_methods
@@ -616,31 +197,8 @@
def without_versioning(&block)
self.class.settings.push(version: nil, version_options: nil)
yield
self.class.settings.pop
- end
-
- # This module extends user defined helpers
- # to provide some API-specific functionality
- module Helpers
- attr_accessor :api
- def params(name, &block)
- @named_params ||= {}
- @named_params.merge! name => block
- end
-
- def api_changed(new_api)
- @api = new_api
- process_named_params
- end
-
- protected
-
- def process_named_params
- if @named_params && @named_params.any?
- api.imbue(:named_params, @named_params)
- end
- end
end
end
end