lib/grape/api.rb in grape-1.2.3 vs lib/grape/api.rb in grape-1.2.4

- old
+ new

@@ -4,11 +4,11 @@ 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 # Class methods that we want to call on the API rather than on the API object - NON_OVERRIDABLE = (Class.new.methods + %i[call call!]).freeze + NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration]).freeze class << self attr_accessor :base_instance, :instances # Rather than initializing an object of type Grape::API, create an object of type Instance @@ -62,12 +62,10 @@ # Alleviates problems with autoloading by tring to search for the constant def const_missing(*args) if base_instance.const_defined?(*args) base_instance.const_get(*args) - elsif parent && parent.const_defined?(*args) - parent.const_get(*args) else super end end @@ -75,21 +73,21 @@ # For instance, a descripcion could be done using: `desc configuration[:description]` 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. def mount_instance(opts = {}) instance = Class.new(@base_parent) - instance.configuration = opts[:configuration] || {} + instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {}) instance.base = self replay_setup_on(instance) instance end # Replays the set up to produce an API as defined in this class, can be called # on classes that inherit from Grape::API def replay_setup_on(instance) - @setup.each do |setup_stage| - instance.send(setup_stage[:method], *setup_stage[:args], &setup_stage[:block]) + @setup.each do |setup_step| + replay_step_on(instance, setup_step) end end def respond_to?(method, include_private = false) super(method, include_private) || base_instance.respond_to?(method, include_private) @@ -110,16 +108,45 @@ private # Adds a new stage to the set up require to get a Grape::API up and running def add_setup(method, *args, &block) - setup_stage = { method: method, args: args, block: block } - @setup << setup_stage + setup_step = { method: method, args: args, block: block } + @setup << setup_step last_response = nil @instances.each do |instance| - last_response = instance.send(setup_stage[:method], *setup_stage[:args], &setup_stage[:block]) + last_response = replay_step_on(instance, setup_step) end last_response + end + + def replay_step_on(instance, setup_step) + return if skip_immediate_run?(instance, setup_step[:args]) + instance.send(setup_step[:method], *evaluate_arguments(instance.configuration, *setup_step[:args]), &setup_step[:block]) + end + + # Skips steps that contain arguments to be lazily executed (on re-mount time) + def skip_immediate_run?(instance, args) + instance.base_instance? && + (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) }) + end + + def any_lazy?(args) + args.any? { |argument| argument.respond_to?(:lazy?) && argument.lazy? } + end + + def evaluate_arguments(configuration, *args) + args.map do |argument| + if argument.respond_to?(:lazy?) && argument.lazy? + configuration.fetch(argument.access_keys).evaluate + elsif argument.is_a?(Hash) + argument.map { |key, value| [key, evaluate_arguments(configuration, value).first] }.to_h + elsif argument.is_a?(Array) + evaluate_arguments(configuration, *argument) + else + argument + end + end end end end end