class Grape::API
should subclass this class in order to build an API.
The API class is the primary entry point for creating Grape APIs. Users
def add_setup(method, *args, &block)
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
def any_lazy?(args)
def any_lazy?(args) args.any? { |argument| argument.respond_to?(:lazy?) && argument.lazy? } end
def call(...)
(http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
the headers, and the body. See [the rack specification]
from Rack and ultimately returns an array of three values: the status,
This is the interface point between Rack and Grape; it accepts a request
def call(...) instance_for_rack.call(...) end
def compile!
def compile! instance_for_rack.compile! # See API::Instance.compile! end
def configure
The configuration set here is accessible from inside an API with
API. If no block is given, returns the configuration hash.
configuration hash to the block which you can use to configure your
Configure an API from the outside. If a block is given, it'll pass a
def configure config = @base_instance.configuration if block_given? yield config self else config end end
def evaluate_arguments(configuration, *args)
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
def inherited(api)
When inherited, will create a list of all instances (times the API was mounted)
def inherited(api) super api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance) api.override_all_methods! end
def initial_setup(base_instance_parent)
Initialize the instance variables on the remountable class, and the base_instance
def initial_setup(base_instance_parent) @instances = [] @setup = [] @base_parent = base_instance_parent @base_instance = mount_instance end
def instance_for_rack
def instance_for_rack if never_mounted? base_instance else mounted_instances.first end end
def method_missing(method, *args, &block)
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
def mount_instance(opts = {})
depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
For instance, a description could be done using: `desc configuration[:description]` if it may vary
The remountable class can have a configuration hash to provide some dynamic class-level variables.
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
def mounted_instances
def mounted_instances instances - [base_instance] end
def never_mounted?
def never_mounted? mounted_instances.empty? end
def new(...)
def new(...) base_instance.new(...) end
def override_all_methods!
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
def replay_setup_on(instance)
Replays the set up to produce an API as defined in this class, can be called
def replay_setup_on(instance) @setup.each do |setup_step| replay_step_on(instance, setup_step) end end
def replay_step_on(instance, setup_step)
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
def respond_to?(method, include_private = false)
def respond_to?(method, include_private = false) super || base_instance.respond_to?(method, include_private) end
def respond_to_missing?(method, include_private = false)
def respond_to_missing?(method, include_private = false) base_instance.respond_to?(method, include_private) end
def skip_immediate_run?(instance, args)
def skip_immediate_run?(instance, args) instance.base_instance? && (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) }) end