class Grape::Endpoint

An Endpoint is the proxy scope in which all routing blocks are executed. In other words, any methods on the instance level of this class may be called from inside a `get`, `post`, etc.

Attributes

block[RW]
env[R]
headers[R]
options[RW]
params[R]
request[R]
settings[RW]
source[RW]

Public Class Methods

before_each(new_setup = false, &block) click to toggle source
# File lib/grape/endpoint.rb, line 11
def before_each(new_setup = false, &block)
  if new_setup == false
    if block_given?
      @before_each = block
    else
      return @before_each
    end
  else
    @before_each = new_setup
  end
end
generate_api_method(method_name, &block) click to toggle source

@api private

Create an UnboundMethod that is appropriate for executing an endpoint route.

The unbound method allows explicit calls to return without raising a LocalJumpError. The method will be removed, but a Proc reference to it will be returned. The returned Proc expects a single argument: the instance of Endpoint to bind to the method during the call.

@param [String, Symbol] method_name @return [Proc] @raise [NameError] an instance method with the same name already exists

# File lib/grape/endpoint.rb, line 36
def generate_api_method(method_name, &block)
  if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s)
    raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
  end
  define_method(method_name, &block)
  method = instance_method(method_name)
  remove_method(method_name)
  proc { |endpoint_instance| method.bind(endpoint_instance).call }
end
new(settings, options = {}, &block) click to toggle source
# File lib/grape/endpoint.rb, line 47
def initialize(settings, options = {}, &block)
  require_option(options, :path)
  require_option(options, :method)

  @settings = settings
  @options = options

  @options[:path] = Array(options[:path])
  @options[:path] << '/' if options[:path].empty?

  @options[:method] = Array(options[:method])
  @options[:route_options] ||= {}

  if block_given?
    @source = block
    @block = self.class.generate_api_method(method_name, &block)
  end
end

Public Instance Methods

body(value = nil) click to toggle source

Allows you to define the response body as something other than the return value.

@example

get '/body' do
  body "Body"
  "Not the Body"
end

GET /body # => "Body"
# File lib/grape/endpoint.rb, line 306
def body(value = nil)
  if value
    @body = value
  else
    @body
  end
end
call(env) click to toggle source
# File lib/grape/endpoint.rb, line 155
def call(env)
  dup.call!(env)
end
call!(env) click to toggle source
# File lib/grape/endpoint.rb, line 159
def call!(env)
  extend helpers

  env['api.endpoint'] = self
  if options[:app]
    options[:app].call(env)
  else
    builder = build_middleware
    builder.run options[:app] || lambda { |arg| run(arg) }
    builder.call(env)
  end
end
compile_path(prepared_path, anchor = true, requirements = {}) click to toggle source
# File lib/grape/endpoint.rb, line 148
def compile_path(prepared_path, anchor = true, requirements = {})
  endpoint_options = {}
  endpoint_options[:version] = /#{settings[:version].join('|')}/ if settings[:version]
  endpoint_options.merge!(requirements)
  Rack::Mount::Strexp.compile(prepared_path, endpoint_options, %w( / . ? ), anchor)
end
content_type(val) click to toggle source

Set response content-type

# File lib/grape/endpoint.rb, line 280
def content_type(val)
  header('Content-Type', val)
end
cookies() click to toggle source

Set or get a cookie

@example

cookies[:mycookie] = 'mycookie val'
cookies['mycookie-string'] = 'mycookie string val'
cookies[:more] = { value: '123', expires: Time.at(0) }
cookies.delete :more
# File lib/grape/endpoint.rb, line 292
def cookies
  @cookies ||= Cookies.new
end
declared(params, options = {}, declared_params = nil) click to toggle source

A filtering method that will return a hash consisting only of keys that have been declared by a `params` statement against the current/target endpoint or parent namespaces

@param params [Hash] The initial hash to filter. Usually this will just be `params` @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces` options. `:include_parent_namespaces` defaults to true, hence must be set to false if you want only to return params declared against the current/target endpoint

# File lib/grape/endpoint.rb, line 181
def declared(params, options = {}, declared_params = nil)
  options[:include_missing] = true unless options.key?(:include_missing)
  options[:include_parent_namespaces] = true unless options.key?(:include_parent_namespaces)
  if declared_params.nil?
    declared_params = !options[:include_parent_namespaces] ? settings[:declared_params] :
      settings.gather(:declared_params)
  end

  unless declared_params
    raise ArgumentError, "Tried to filter for declared parameters but none exist."
  end

  if params.is_a? Array
    params.map do |param|
      declared(param || {}, options, declared_params)
    end
  else
    declared_params.inject({}) do |hash, key|
      key = { key => nil } unless key.is_a? Hash

      key.each_pair do |parent, children|
        output_key = options[:stringify] ? parent.to_s : parent.to_sym
        if params.key?(parent) || options[:include_missing]
          hash[output_key] = if children
                               declared(params[parent] || {}, options, Array(children))
                             else
                               params[parent]
                             end
        end
      end

      hash
    end
  end
end
endpoints() click to toggle source

Return the collection of endpoints within this endpoint. This is the case when an Grape::API mounts another Grape::API.

# File lib/grape/endpoint.rb, line 382
def endpoints
  if options[:app] && options[:app].respond_to?(:endpoints)
    options[:app].endpoints
  else
    nil
  end
end
error!(message, status = nil, headers = nil) click to toggle source

End the request and display an error to the end user with the specified message.

@param message [String] The message to display. @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.

# File lib/grape/endpoint.rb, line 227
def error!(message, status = nil, headers = nil)
  status = settings[:default_error_status] unless status
  throw :error, message: message, status: status, headers: headers
end
header(key = nil, val = nil) click to toggle source

Set an individual header or retrieve all headers that have been set.

# File lib/grape/endpoint.rb, line 271
def header(key = nil, val = nil)
  if key
    val ? @header[key.to_s] = val : @header.delete(key.to_s)
  else
    @header
  end
end
method_name() click to toggle source
# File lib/grape/endpoint.rb, line 70
def method_name
  [options[:method],
   Namespace.joined_space(settings),
   settings.gather(:mount_path).join('/'),
   options[:path].join('/')
 ].join(" ")
end
mount_in(route_set) click to toggle source
# File lib/grape/endpoint.rb, line 82
def mount_in(route_set)
  if endpoints
    endpoints.each { |e| e.mount_in(route_set) }
  else
    routes.each do |route|
      methods = [route.route_method]
      if !settings[:do_not_route_head] && route.route_method == "GET"
        methods << "HEAD"
      end
      methods.each do |method|
        route_set.add_route(self, {
                              path_info: route.route_compiled,
                              request_method: method
        },  route_info: route)
      end
    end
  end
end
namespace() click to toggle source
# File lib/grape/endpoint.rb, line 144
def namespace
  @namespace ||= Namespace.joined_space_path(settings)
end
prepare_path(path) click to toggle source
# File lib/grape/endpoint.rb, line 140
def prepare_path(path)
  Path.prepare(path, namespace, settings)
end
prepare_routes() click to toggle source
# File lib/grape/endpoint.rb, line 101
def prepare_routes
  routes = []
  options[:method].each do |method|
    options[:path].each do |path|
      prepared_path = prepare_path(path)

      anchor = options[:route_options][:anchor]
      anchor = anchor.nil? ? true : anchor

      endpoint_requirements = options[:route_options][:requirements] || {}
      all_requirements = (settings.gather(:namespace).map(&:requirements) << endpoint_requirements)
      requirements = all_requirements.reduce({}) do |base_requirements, single_requirements|
        base_requirements.merge!(single_requirements)
      end

      path = compile_path(prepared_path, anchor && !options[:app], requirements)
      regex = Rack::Mount::RegexpWithNamedGroups.new(path)
      path_params = {}
      # named parameters in the api path
      named_params = regex.named_captures.map { |nc| nc[0] } - %w(version format)
      named_params.each { |named_param| path_params[named_param] = "" }
      # route parameters declared via desc or appended to the api declaration
      route_params = (options[:route_options][:params] || {})
      path_params.merge!(route_params)
      request_method = (method.to_s.upcase unless method == :any)
      routes << Route.new(options[:route_options].clone.merge(
        prefix: settings[:root_prefix],
        version: settings[:version] ? settings[:version].join('|') : nil,
        namespace: namespace,
        method: request_method,
        path: prepared_path,
        params: path_params,
        compiled: path
      ))
    end
  end
  routes
end
present(*args) click to toggle source

Allows you to make use of Grape Entities by setting the response body to the serializable hash of the entity provided in the `:with` option. This has the added benefit of automatically passing along environment and version information to the serialization, making it very easy to do conditional exposures. See Entity docs for more info.

@example

get '/users/:id' do
  present User.find(params[:id]),
    with: API::Entities::User,
    admin: current_user.admin?
end
# File lib/grape/endpoint.rb, line 329
def present(*args)
  options = args.count > 1 ? args.extract_options! : {}
  key, object = if args.count == 2 && args.first.is_a?(Symbol)
                  args
                else
                  [nil, args.first]
                end
  entity_class = options.delete(:with)

  if entity_class.nil?
    # entity class not explicitely defined, auto-detect from relation#klass or first object in the collection
    object_class = if object.respond_to?(:klass)
                     object.klass
                   else
                     object.respond_to?(:first) ? object.first.class : object.class
                   end

    object_class.ancestors.each do |potential|
      entity_class ||= (settings[:representations] || {})[potential]
    end

    entity_class ||= object_class.const_get(:Entity) if object_class.const_defined?(:Entity) && object_class.const_get(:Entity).respond_to?(:represent)
  end

  root = options.delete(:root)

  representation = if entity_class
                     embeds = { env: env }
                     embeds[:version] = env['api.version'] if env['api.version']
                     entity_class.represent(object, embeds.merge(options))
                   else
                     object
                   end

  representation = { root => representation } if root
  representation = (@body || {}).merge(key => representation) if key
  body representation
end
redirect(url, options = {}) click to toggle source

Redirect to a new url.

@param url [String] The url to be redirect. @param options [Hash] The options used when redirect.

:permanent, default true.
# File lib/grape/endpoint.rb, line 237
def redirect(url, options = {})
  merged_options = { permanent: false }.merge(options)
  if merged_options[:permanent]
    status 301
  else
    if env['HTTP_VERSION'] == 'HTTP/1.1' && request.request_method.to_s.upcase != "GET"
      status 303
    else
      status 302
    end
  end
  header "Location", url
  body ""
end
require_option(options, key) click to toggle source
# File lib/grape/endpoint.rb, line 66
def require_option(options, key)
  raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
end
route() click to toggle source

Returns route information for the current request.

@example

desc "Returns the route description."
get '/' do
  route.route_description
end
# File lib/grape/endpoint.rb, line 376
def route
  env["rack.routing_args"][:route_info]
end
routes() click to toggle source
# File lib/grape/endpoint.rb, line 78
def routes
  @routes ||= endpoints ? endpoints.collect(&:routes).flatten : prepare_routes
end
status(status = nil) click to toggle source

Set or retrieve the HTTP status code.

@param status [Integer] The HTTP Status Code to return for this request.

# File lib/grape/endpoint.rb, line 255
def status(status = nil)
  if status
    @status = status
  else
    return @status if @status
    case request.request_method.to_s.upcase
    when 'POST'
      201
    else
      200
    end
  end
end
version() click to toggle source

The API version as specified in the URL.

# File lib/grape/endpoint.rb, line 218
def version
  env['api.version']
end

Protected Instance Methods

after_validations() click to toggle source
# File lib/grape/endpoint.rb, line 525
def after_validations
  aggregate_setting(:after_validations)
end
afters() click to toggle source
# File lib/grape/endpoint.rb, line 529
def afters
  aggregate_setting(:afters)
end
aggregate_setting(key) click to toggle source
# File lib/grape/endpoint.rb, line 499
def aggregate_setting(key)
  settings.stack.inject([]) do |aggregate, frame|
    aggregate + (frame[key] || [])
  end
end
before_validations() click to toggle source
# File lib/grape/endpoint.rb, line 521
def before_validations
  aggregate_setting(:before_validations)
end
befores() click to toggle source
# File lib/grape/endpoint.rb, line 517
def befores
  aggregate_setting(:befores)
end
build_middleware() click to toggle source
# File lib/grape/endpoint.rb, line 431
def build_middleware
  b = Rack::Builder.new

  b.use Rack::Head
  b.use Grape::Middleware::Error,
        format: settings[:format],
        content_types: settings[:content_types],
        default_status: settings[:default_error_status] || 500,
        rescue_all: settings[:rescue_all],
        default_error_formatter: settings[:default_error_formatter],
        error_formatters: settings[:error_formatters],
        rescue_options: settings[:rescue_options],
        rescue_handlers: merged_setting(:rescue_handlers),
        base_only_rescue_handlers: merged_setting(:base_only_rescue_handlers),
        all_rescue_handler: settings[:all_rescue_handler]

  aggregate_setting(:middleware).each do |m|
    m = m.dup
    block = m.pop if m.last.is_a?(Proc)
    if block
      b.use(*m, &block)
    else
      b.use(*m)
    end
  end

  if settings[:auth]
    auth_proc         = settings[:auth][:proc]
    auth_proc_context = self
    auth_middleware   = {
      http_basic:   { class: Rack::Auth::Basic,       args: [settings[:auth][:realm]] },
      http_digest:  { class: Rack::Auth::Digest::MD5, args: [settings[:auth][:realm], settings[:auth][:opaque]] }
    }[settings[:auth][:type]]

    # evaluate auth proc in context of endpoint
    if auth_middleware
      b.use auth_middleware[:class], *auth_middleware[:args] do |*args|
        auth_proc_context.instance_exec(*args, &auth_proc)
      end
    end
  end

  if settings[:version]
    b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]),
          versions: settings[:version] ? settings[:version].flatten : nil,
          version_options: settings[:version_options],
          prefix: settings[:root_prefix]

  end

  b.use Grape::Middleware::Formatter,
        format: settings[:format],
        default_format: settings[:default_format] || :txt,
        content_types: settings[:content_types],
        formatters: settings[:formatters],
        parsers: settings[:parsers]

  b
end
helpers() click to toggle source
# File lib/grape/endpoint.rb, line 491
def helpers
  m = Module.new
  settings.stack.each do |frame|
    m.send :include, frame[:helpers] if frame[:helpers]
  end
  m
end
merged_setting(key) click to toggle source
# File lib/grape/endpoint.rb, line 505
def merged_setting(key)
  settings.stack.inject({}) do |merged, frame|
    merged.merge(frame[key] || {})
  end
end
run(env) click to toggle source
# File lib/grape/endpoint.rb, line 392
def run(env)
  @env = env
  @header = {}

  @request = Grape::Request.new(env)
  @params = @request.params
  @headers = @request.headers

  cookies.read(@request)

  self.class.before_each.call(self) if self.class.before_each

  run_filters befores

  run_filters before_validations

  # Retrieve validations from this namespace and all parent namespaces.
  validation_errors = []
  settings.gather(:validations).each do |validator|
    begin
      validator.validate!(params)
    rescue Grape::Exceptions::Validation => e
      validation_errors << e
    end
  end

  if validation_errors.any?
    raise Grape::Exceptions::ValidationErrors, errors: validation_errors
  end

  run_filters after_validations

  response_text = @block ? @block.call(self) : nil
  run_filters afters
  cookies.write(header)

  [status, header, [body || response_text]]
end
run_filters(filters) click to toggle source
# File lib/grape/endpoint.rb, line 511
def run_filters(filters)
  (filters || []).each do |filter|
    instance_eval(&filter)
  end
end