Class: Grape::API

Inherits:
Object
  • Object
show all
Extended by:
Validations::ClassMethods
Defined in:
lib/grape/api.rb,
lib/grape/validations/coerce.rb

Overview

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

Defined Under Namespace

Modules: Helpers

Constant Summary

LOCK =
Mutex.new
Boolean =

rubocop:disable ConstantName

Virtus::Attribute::Boolean

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Validations::ClassMethods

document_attribute, params, reset_validations!

Constructor Details

#initializeAPI



527
528
529
530
531
532
533
534
# File 'lib/grape/api.rb', line 527

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

Class Attribute Details

.endpointsObject (readonly)

Returns the value of attribute endpoints



9
10
11
# File 'lib/grape/api.rb', line 9

def endpoints
  @endpoints
end

.instanceObject (readonly)

Returns the value of attribute instance



9
10
11
# File 'lib/grape/api.rb', line 9

def instance
  @instance
end

.logger(logger = nil) ⇒ Object



14
15
16
17
18
19
20
# File 'lib/grape/api.rb', line 14

def logger(logger = nil)
  if logger
    @logger = logger
  else
    @logger ||= Logger.new($stdout)
  end
end

.route_setObject (readonly)

Returns the value of attribute route_set



9
10
11
# File 'lib/grape/api.rb', line 9

def route_set
  @route_set
end

.routesObject (readonly)

An array of API routes.



465
466
467
# File 'lib/grape/api.rb', line 465

def routes
  @routes
end

.settingsObject (readonly)

Returns the value of attribute settings



9
10
11
# File 'lib/grape/api.rb', line 9

def settings
  @settings
end

.versionsObject (readonly)

Returns the value of attribute versions



9
10
11
# File 'lib/grape/api.rb', line 9

def versions
  @versions
end

Class Method Details

.after(&block) ⇒ Object



373
374
375
# File 'lib/grape/api.rb', line 373

def after(&block)
  imbue(:afters, [block])
end

.after_validation(&block) ⇒ Object



369
370
371
# File 'lib/grape/api.rb', line 369

def after_validation(&block)
  imbue(:after_validations, [block])
end

.auth(type = nil, options = {}, &block) ⇒ Object

Add an authentication type to the API. Currently only :http_basic, :http_digest and :oauth2 are supported.



296
297
298
299
300
301
302
# File 'lib/grape/api.rb', line 296

def auth(type = nil, options = {}, &block)
  if type
    set(:auth, { type: type.to_sym, proc: block }.merge(options))
  else
    settings[:auth]
  end
end

.before(&block) ⇒ Object



361
362
363
# File 'lib/grape/api.rb', line 361

def before(&block)
  imbue(:befores, [block])
end

.before_validation(&block) ⇒ Object



365
366
367
# File 'lib/grape/api.rb', line 365

def before_validation(&block)
  imbue(:before_validations, [block])
end

.call(env) ⇒ Object



38
39
40
41
# File 'lib/grape/api.rb', line 38

def call(env)
  LOCK.synchronize { compile } unless instance
  call!(env)
end

.call!(env) ⇒ Object



43
44
45
# File 'lib/grape/api.rb', line 43

def call!(env)
  instance.call(env)
end

.cascade(value = nil) ⇒ Object



473
474
475
476
477
478
479
# File 'lib/grape/api.rb', line 473

def cascade(value = nil)
  if value.nil?
    settings.has_key?(:cascade) ? !!settings[:cascade] : true
  else
    set(:cascade, value)
  end
end

.change!Object



34
35
36
# File 'lib/grape/api.rb', line 34

def change!
  @instance = nil
end

.compileObject



30
31
32
# File 'lib/grape/api.rb', line 30

def compile
  @instance ||= new
end

.content_type(key, val) ⇒ Object

Specify additional content-types, e.g.: content_type :xls, 'application/vnd.ms-excel'



173
174
175
# File 'lib/grape/api.rb', line 173

def content_type(key, val)
  settings.imbue(:content_types, key.to_sym => val)
end

.content_typesObject

All available content types.



178
179
180
# File 'lib/grape/api.rb', line 178

def content_types
  Grape::ContentTypes.content_types_for(settings[:content_types])
end

.default_error_formatter(new_formatter_name = nil) ⇒ Object

Specify a default error formatter.



152
153
154
155
156
157
158
159
# 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

.default_error_status(new_status = nil) ⇒ Object

Specify the default status code for errors.



183
184
185
# 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

.default_format(new_format = nil) ⇒ Object

Specify the default format for the API's serializers. May be :json or :txt (default).



121
122
123
# 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

.delete(paths = ['/'], options = {}, &block) ⇒ Object



393
394
395
# File 'lib/grape/api.rb', line 393

def delete(paths = ['/'], options = {}, &block)
  route('DELETE', paths, options, &block)
end

.desc(description, options = {}) ⇒ Object

Add a description to the next namespace or function.



115
116
117
# File 'lib/grape/api.rb', line 115

def desc(description, options = {})
  @last_description = options.merge(description: description)
end

.do_not_route_head!Object

Do not route HEAD requests to GET requests automatically



70
71
72
# File 'lib/grape/api.rb', line 70

def do_not_route_head!
  set(:do_not_route_head, true)
end

.do_not_route_options!Object

Do not automatically route OPTIONS



75
76
77
# File 'lib/grape/api.rb', line 75

def do_not_route_options!
  set(:do_not_route_options, true)
end

.error_formatter(format, options) ⇒ Object



161
162
163
164
165
166
167
168
169
# File 'lib/grape/api.rb', line 161

def error_formatter(format, options)
  if options.is_a?(Hash) && options.has_key?(:with)
    formatter = options[:with]
  else
    formatter = options
  end

  settings.imbue(:error_formatters, format.to_sym => formatter)
end

.format(new_format = nil) ⇒ Object

Specify the format for the API's serializers. May be :json, :xml, :txt, etc.



127
128
129
130
131
132
133
134
135
136
137
138
139
# 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

.formatter(content_type, new_formatter) ⇒ Object

Specify a custom formatter for a content-type.



142
143
144
# File 'lib/grape/api.rb', line 142

def formatter(content_type, new_formatter)
  settings.imbue(:formatters, content_type.to_sym => new_formatter)
end

.get(paths = ['/'], options = {}, &block) ⇒ Object



377
378
379
# File 'lib/grape/api.rb', line 377

def get(paths = ['/'], options = {}, &block)
  route('GET', paths, options, &block)
end

.head(paths = ['/'], options = {}, &block) ⇒ Object



389
390
391
# File 'lib/grape/api.rb', line 389

def head(paths = ['/'], options = {}, &block)
  route('HEAD', paths, options, &block)
end

.helpers(new_mod = nil, &block) ⇒ Object

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.

Examples:

Define some helpers.


class ExampleAPI < Grape::API
  helpers do
    def current_user
      User.find_by_id(params[:token])
    end
  end
end


269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/grape/api.rb', line 269

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

.http_basic(options = {}, &block) ⇒ Object

Add HTTP Basic authorization to the API.

Options Hash (options):

  • :realm (String)

    "API Authorization" The HTTP Basic realm.



308
309
310
311
# File 'lib/grape/api.rb', line 308

def http_basic(options = {}, &block)
  options[:realm] ||= "API Authorization"
  auth :http_basic, options, &block
end

.http_digest(options = {}, &block) ⇒ Object



313
314
315
316
317
# File 'lib/grape/api.rb', line 313

def http_digest(options = {}, &block)
  options[:realm] ||= "API Authorization"
  options[:opaque] ||= "secret"
  auth :http_digest, options, &block
end

.imbue(key, value) ⇒ Object

Add to a configuration value for this namespace.



60
61
62
# File 'lib/grape/api.rb', line 60

def imbue(key, value)
  settings.imbue(key, value)
end

.middlewareObject

Retrieve an array of the middleware classes and arguments that are currently applied to the application.



457
458
459
460
461
462
# File 'lib/grape/api.rb', line 457

def middleware
  settings.stack.inject([]) do |a, s|
    a += s[:middleware] if s[:middleware]
    a
  end
end

.mount(mounts) ⇒ Object



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/grape/api.rb', line 319

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

.namespace(space = nil, options = {}, &block) ⇒ Object Also known as: group, resource, resources, segment



405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/grape/api.rb', line 405

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

.options(paths = ['/'], options = {}, &block) ⇒ Object



397
398
399
# File 'lib/grape/api.rb', line 397

def options(paths = ['/'], options = {}, &block)
  route('OPTIONS', paths, options, &block)
end

.parser(content_type, new_parser) ⇒ Object

Specify a custom parser for a content-type.



147
148
149
# File 'lib/grape/api.rb', line 147

def parser(content_type, new_parser)
  settings.imbue(:parsers, content_type.to_sym => new_parser)
end

.patch(paths = ['/'], options = {}, &block) ⇒ Object



401
402
403
# File 'lib/grape/api.rb', line 401

def patch(paths = ['/'], options = {}, &block)
  route('PATCH', paths, options, &block)
end

.post(paths = ['/'], options = {}, &block) ⇒ Object



381
382
383
# File 'lib/grape/api.rb', line 381

def post(paths = ['/'], options = {}, &block)
  route('POST', paths, options, &block)
end

.prefix(prefix = nil) ⇒ Object

Define a root URL prefix for your entire API.



65
66
67
# File 'lib/grape/api.rb', line 65

def prefix(prefix = nil)
  prefix ? set(:root_prefix, prefix) : settings[:root_prefix]
end

.put(paths = ['/'], options = {}, &block) ⇒ Object



385
386
387
# File 'lib/grape/api.rb', line 385

def put(paths = ['/'], options = {}, &block)
  route('PUT', paths, options, &block)
end

.represent(model_class, options) ⇒ Object

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.

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.

Examples:

class ExampleAPI < Grape::API
  represent User, with: Entity::User

  get '/me' do
    present current_user # with: Entity::User is assumed
  end
end

Options Hash (options):

  • :with (Class)

    The entity class that will represent the model.

Raises:



245
246
247
248
# File 'lib/grape/api.rb', line 245

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

.rescue_from(*exception_classes, options = {}) ⇒ Object

Allows you to rescue certain exceptions that occur to return a grape error rather than raising all the way to the server level.

Examples:

Rescue from custom exceptions

class ExampleAPI < Grape::API
  class CustomError < StandardError; end

  rescue_from CustomError
end

Options Hash (options):

  • :backtrace (Boolean)

    Include a backtrace in the rescue response.

  • :rescue_subclasses (Boolean)

    Also rescue subclasses of exception classes



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# 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.has_key?(:with)

  handler_type = !!options[:rescue_subclasses] ? :rescue_handlers : :base_only_rescue_handlers
  imbue handler_type, Hash[args.map { |arg| [arg, handler] }]

  imbue(:rescue_options, options)

  set(:rescue_all, true) if args.include?(:all)
end

.reset!Object



22
23
24
25
26
27
28
# 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

.route(methods, paths = ['/'], route_options = {}, &block) ⇒ Object

Defines a route that will be recognized by the Grape API.

Examples:

Defining a basic route.

class MyAPI < Grape::API
  route(:any, '/hello') do
    {hello: 'world'}
  end
end


349
350
351
352
353
354
355
356
357
358
359
# File 'lib/grape/api.rb', line 349

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

.route_param(param, options = {}, &block) ⇒ Object

Thie method allows you to quickly define a parameter route segment in your API.

Options Hash (options):

  • You (Regexp)

    may supply a regular expression that the declared parameter must meet.



424
425
426
427
428
# File 'lib/grape/api.rb', line 424

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

.scope(name = nil, &block) ⇒ Object

Create a scope without affecting the URL.



438
439
440
# File 'lib/grape/api.rb', line 438

def scope(name = nil, &block)
  nest(block)
end

.set(key, value) ⇒ Object

Set a configuration value for this namespace.



51
52
53
# File 'lib/grape/api.rb', line 51

def set(key, value)
  settings[key.to_sym] = value
end

.use(middleware_class, *args, &block) ⇒ Object

Apply a custom middleware to the API. Applies to the current namespace and any children, but not parents.



448
449
450
451
452
# File 'lib/grape/api.rb', line 448

def use(middleware_class, *args, &block)
  arr = [middleware_class, *args]
  arr << block if block_given?
  imbue(:middleware, [arr])
end

.version(*args, &block) ⇒ Object

Specify an API version.

Examples:

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


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# 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.has_key?(:vendor)

    @versions = versions | args
    nest(block) do
      set(:version, args)
      set(:version_options, options)
    end
  end

  @versions.last unless @versions.nil?
end

Instance Method Details

#call(env) ⇒ Object



536
537
538
539
540
# File 'lib/grape/api.rb', line 536

def call(env)
  status, headers, body = @route_set.call(env)
  headers.delete('X-Cascade') unless cascade?
  [status, headers, body]
end

#cascade?Boolean

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.



550
551
552
553
554
# File 'lib/grape/api.rb', line 550

def cascade?
  return !!self.class.settings[:cascade] if self.class.settings.has_key?(:cascade)
  return !!self.class.settings[:version_options][:cascade] if self.class.settings[:version_options] && self.class.settings[:version_options].has_key?(:cascade)
  true
end