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
Attributes
Public Class Methods
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. NOTE: This will only be called on an API
directly mounted on RACK
# File lib/grape/api.rb, line 77 def call(...) instance_for_rack.call(...) end
# File lib/grape/api.rb, line 127 def compile! instance_for_rack.compile! # See API::Instance.compile! end
Configure an API
from the outside. If a block is given, it’ll pass a configuration hash to the block which you can use to configure your API
. If no block is given, returns the configuration hash. The configuration set here is accessible from inside an API
with ‘configuration` as normal.
# File lib/grape/api.rb, line 62 def configure config = @base_instance.configuration if block_given? yield config self else config end end
Alleviates problems with autoloading by tring to search for the constant
# File lib/grape/api.rb, line 82 def const_missing(*args) if base_instance.const_defined?(*args) base_instance.const_get(*args) else super end end
When inherited, will create a list of all instances (times the API
was mounted) It will listen to the setup required to mount that endpoint, and replicate it on any new instance
# File lib/grape/api.rb, line 32 def inherited(api) super api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance) api.override_all_methods! end
Initialize the instance variables on the remountable class, and the base_instance
an instance that will be used to create the set up but will not be mounted
# File lib/grape/api.rb, line 41 def initial_setup(base_instance_parent) @instances = [] @setup = Set.new @base_parent = base_instance_parent @base_instance = mount_instance end
# File lib/grape/api.rb, line 118 def method_missing(method, *args, &block) # If there's a missing method, it may be defined on the base_instance instead. if respond_to_missing?(method) base_instance.send(method, *args, &block) else super end end
The remountable class can have a configuration hash to provide some dynamic class-level variables. For instance, a description could be done using: ‘desc configuration` if it may vary depending on where the endpoint is mounted. Use with care, if you find yourself using configuration too much, you may actually want to provide a new API
rather than remount it.
# File lib/grape/api.rb, line 94 def mount_instance(**opts) instance = Class.new(@base_parent) instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {}) instance.base = self replay_setup_on(instance) instance end
Rather than initializing an object of type Grape::API
, create an object of type Instance
# File lib/grape/api.rb, line 26 def new(...) base_instance.new(...) end
Redefines all methods so that are forwarded to add_setup
and be recorded
# File lib/grape/api.rb, line 49 def override_all_methods! (base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override| define_singleton_method(method_override) do |*args, &block| add_setup(method_override, *args, &block) end end end
Replays the set up to produce an API
as defined in this class, can be called on classes that inherit from Grape::API
# File lib/grape/api.rb, line 104 def replay_setup_on(instance) @setup.each do |setup_step| replay_step_on(instance, setup_step) end end
# File lib/grape/api.rb, line 110 def respond_to?(method, include_private = false) super || base_instance.respond_to?(method, include_private) end
# File lib/grape/api.rb, line 114 def respond_to_missing?(method, include_private = false) base_instance.respond_to?(method, include_private) end
Private Class Methods
Adds a new stage to the set up require to get a Grape::API
up and running
# File lib/grape/api.rb, line 142 def add_setup(method, *args, &block) setup_step = { method: method, args: args, block: block } @setup += [setup_step] last_response = nil @instances.each do |instance| last_response = replay_step_on(instance, setup_step) end # Updating all previously mounted classes in the case that new methods have been executed. if method != :mount && @setup.any? previous_mount_steps = @setup.select { |step| step[:method] == :mount } previous_mount_steps.each do |mount_step| refresh_mount_step = mount_step.merge(method: :refresh_mounted_api) @setup += [refresh_mount_step] @instances.each do |instance| replay_step_on(instance, refresh_mount_step) end end end last_response end
# File lib/grape/api.rb, line 183 def any_lazy?(args) args.any? { |argument| argument.respond_to?(:lazy?) && argument.lazy? } end
# File lib/grape/api.rb, line 187 def evaluate_arguments(configuration, *args) args.map do |argument| if argument.respond_to?(:lazy?) && argument.lazy? argument.evaluate_from(configuration) elsif argument.is_a?(Hash) argument.transform_values { |value| evaluate_arguments(configuration, value).first } elsif argument.is_a?(Array) evaluate_arguments(configuration, *argument) else argument end end end
# File lib/grape/api.rb, line 133 def instance_for_rack if never_mounted? base_instance else mounted_instances.first end end
# File lib/grape/api.rb, line 205 def mounted_instances instances - [base_instance] end
# File lib/grape/api.rb, line 201 def never_mounted? mounted_instances.empty? end
# File lib/grape/api.rb, line 165 def replay_step_on(instance, setup_step) return if skip_immediate_run?(instance, setup_step[:args]) args = evaluate_arguments(instance.configuration, *setup_step[:args]) response = instance.send(setup_step[:method], *args, &setup_step[:block]) if skip_immediate_run?(instance, [response]) response else evaluate_arguments(instance.configuration, response).first end end
Skips steps that contain arguments to be lazily executed (on re-mount time)
# File lib/grape/api.rb, line 178 def skip_immediate_run?(instance, args) instance.base_instance? && (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) }) end