module GraphQL::Execution::Instrumentation

def self.apply_instrumenters(multiplex)

- If a `before_query` hook raises an error, subsequent `before_query` hooks will not run (on any query)
- If a `before_multiplex` hook raises an error, no `before_query` hooks will run
Partial runs of instrumentation are possible:

- If an error was capture, it's re-raised after all hooks are finished
- The first raised error is captured; later errors are ignored
- Subsequent `after_` hooks _are_ called
When errors are raised from `after_` hooks:

- If the `before_` hook did _not_ run, the `after_` hook will not be called.
- If a `before_` hook returned without an error, its corresponding `after_` hook will run.
- Instrumenters are a stack; the first `before_query` will have the last `after_query`

This function implements the instrumentation policy:
def self.apply_instrumenters(multiplex)
  schema = multiplex.schema
  queries = multiplex.queries
  query_instrumenters = schema.instrumenters[:query]
  multiplex_instrumenters = schema.instrumenters[:multiplex]
  # First, run multiplex instrumentation, then query instrumentation for each query
  call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
    each_query_call_hooks(query_instrumenters, queries) do
      # Let them be executed
      yield
    end
  end
end

def call_after_hooks(instrumenters, object, after_hook_name, ex)

def call_after_hooks(instrumenters, object, after_hook_name, ex)
  instrumenters.reverse_each do |instrumenter|
    begin
      instrumenter.public_send(after_hook_name, object)
    rescue => e
      ex = e
    end
  end
  ex
end

def call_hooks(instrumenters, object, before_hook_name, after_hook_name)

If they don't all succeed, call after_ for each one that succeeded.
Call each before hook, and if they all succeed, yield.
def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
  begin
    successful = []
    instrumenters.each do |instrumenter|
      instrumenter.public_send(before_hook_name, object)
      successful << instrumenter
    end
    # if any before hooks raise an exception, quit calling before hooks,
    # but call the after hooks on anything that succeeded but also
    # raise the exception that came from the before hook.
  rescue GraphQL::ExecutionError => err
    object.context.errors << err
  rescue => e
    raise call_after_hooks(successful, object, after_hook_name, e)
  end
  begin
    yield # Call the user code
  ensure
    ex = call_after_hooks(successful, object, after_hook_name, nil)
    raise ex if ex
  end
end

def each_query_call_hooks(instrumenters, queries, i = 0)

`call_hooks` takes care of appropriate cleanup.
Then yield if no errors.
Call the before_ hooks of each query,
def each_query_call_hooks(instrumenters, queries, i = 0)
  if i >= queries.length
    yield
  else
    query = queries[i]
    call_hooks(instrumenters, query, :before_query, :after_query) {
      each_query_call_hooks(instrumenters, queries, i + 1) {
        yield
      }
    }
  end
end