class GraphQL::Execution::Lookahead

end
end
fetch_preview_articles(object)
else
fetch_full_articles(object)
if lookahead.selects?(:full_content)
def articles(lookahead:)
# we make the expensive database call instead of the cheap one.
# we can look ahead to see if we need that field. If we do,
# Imagine that full fetch must be made to satisfy ‘fullContent`,
#
# may be issued when only some fields are requested.
# For example, imagine a faster database call
extras: [:lookahead]
field :articles, [Types::Article], null: false,
@example looking ahead in a field
to its configuration.
A field may get access to its lookahead by adding `extras: [:lookahead]`
during execution, but if you’re using it directly, be sure to validate first.)
It assumes that the AST it’s working with is valid. (So, it’s safe to use
Lookahead creates a uniform interface to inspect the forthcoming selections.

def arguments

Returns:
  • (Hash) -
def arguments
  @arguments ||= @field && ArgumentHelpers.arguments(@query, nil, @field, ast_nodes.first)
end

def arguments_match?(arguments, field_defn, field_node)

def arguments_match?(arguments, field_defn, field_node)
  query_kwargs = ArgumentHelpers.arguments(@query, nil, field_defn, field_node)
  arguments.all? do |arg_name, arg_value|
    arg_name = normalize_keyword(arg_name)
    # Make sure the constraint is present with a matching value
    query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
  end
end

def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)

and matches the `arguments:` constraints, then add that node to `matches`
If a selection on `node` matches `field_name` (which is backed by `field_defn`)
def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)
  case node
  when GraphQL::Language::Nodes::Field
    if node.name == field_name
      if arguments.nil? || arguments.empty?
        # No constraint applied
        matches << node
      elsif arguments_match?(arguments, field_defn, node)
        matches << node
      end
    end
  when GraphQL::Language::Nodes::InlineFragment
    node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
  when GraphQL::Language::Nodes::FragmentSpread
    frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
    frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
  else
    raise "Unexpected selection comparison on #{node.class.name} (#{node})"
  end
end

def find_selections(subselections_by_type, selections_on_type, selected_type, ast_selections, arguments)

def find_selections(subselections_by_type, selections_on_type, selected_type, ast_selections, arguments)
  ast_selections.each do |ast_selection|
    case ast_selection
    when GraphQL::Language::Nodes::Field
      response_key = ast_selection.alias || ast_selection.name
      if selections_on_type.key?(response_key)
        selections_on_type[response_key] << ast_selection
      elsif arguments.nil? || arguments.empty?
        selections_on_type[response_key] = [ast_selection]
      else
        field_defn = FieldHelpers.get_field(@query.schema, selected_type, ast_selection.name)
        if arguments_match?(arguments, field_defn, ast_selection)
          selections_on_type[response_key] = [ast_selection]
        end
      end
    when GraphQL::Language::Nodes::InlineFragment
      on_type = selected_type
      subselections_on_type = selections_on_type
      if (t = ast_selection.type)
        # Assuming this is valid, that `t` will be found.
        on_type = @query.schema.get_type(t.name).type_class
        subselections_on_type = subselections_by_type[on_type] ||= {}
      end
      find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
    when GraphQL::Language::Nodes::FragmentSpread
      frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
      # Again, assuming a valid AST
      on_type = @query.schema.get_type(frag_defn.type.name).type_class
      subselections_on_type = subselections_by_type[on_type] ||= {}
      find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
    else
      raise "Invariant: Unexpected selection type: #{ast_selection.class}"
    end
  end
end

def initialize(query:, ast_nodes:, field: nil, root_type: nil, owner_type: nil)

Parameters:
  • root_type (Class) -- if `ast_nodes` are operation definition, this is the root type for that operation
  • field (GraphQL::Schema::Field) -- if `ast_nodes` are fields, this is the field definition matching those nodes
  • ast_nodes (Array, Array) --
  • query (GraphQL::Query) --
def initialize(query:, ast_nodes:, field: nil, root_type: nil, owner_type: nil)
  @ast_nodes = ast_nodes.freeze
  @field = field
  @root_type = root_type
  @query = query
  @selected_type = @field ? @field.type.unwrap : root_type
  @owner_type = owner_type
end

def inspect

def inspect
  "#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>"
end

def name

Returns:
  • (Symbol) -

Other tags:
    Example: getting the name of a selection -
def name
  @field && @field.original_name
end

def normalize_keyword(keyword)

def normalize_keyword(keyword)
  if keyword.is_a?(String)
    Schema::Member::BuildType.underscore(keyword).to_sym
  else
    keyword
  end
end

def normalize_name(name)

If it's a symbol, stringify and camelize it
def normalize_name(name)
  if name.is_a?(Symbol)
    Schema::Member::BuildType.camelize(name.to_s)
  else
    name
  end
end

def selected?

Returns:
  • (Boolean) - True if this lookahead represents a field that was requested
def selected?
  true
end

def selection(field_name, selected_type: @selected_type, arguments: nil)

Returns:
  • (GraphQL::Execution::Lookahead) -
def selection(field_name, selected_type: @selected_type, arguments: nil)
  next_field_name = normalize_name(field_name)
  next_field_defn = FieldHelpers.get_field(@query.schema, selected_type, next_field_name)
  if next_field_defn
    next_nodes = []
    @ast_nodes.each do |ast_node|
      ast_node.selections.each do |selection|
        find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
      end
    end
    if next_nodes.any?
      Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
    else
      NULL_LOOKAHEAD
    end
  else
    NULL_LOOKAHEAD
  end
end

def selections(arguments: nil)

Returns:
  • (Array) -

Parameters:
  • arguments (Hash) -- Arguments which must match in the selection

Other tags:
    Example: getting the name of a selection -
def selections(arguments: nil)
  subselections_by_type = {}
  subselections_on_type = subselections_by_type[@selected_type] = {}
  @ast_nodes.each do |node|
    find_selections(subselections_by_type, subselections_on_type, @selected_type, node.selections, arguments)
  end
  subselections = []
  subselections_by_type.each do |type, ast_nodes_by_response_key|
    ast_nodes_by_response_key.each do |response_key, ast_nodes|
      field_defn = FieldHelpers.get_field(@query.schema, type, ast_nodes.first.name)
      lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
      subselections.push(lookahead)
    end
  end
  subselections
end

def selects?(field_name, arguments: nil)

Returns:
  • (Boolean) -

Parameters:
  • arguments (Hash) -- Arguments which must match in the selection
  • field_name (String, Symbol) --
def selects?(field_name, arguments: nil)
  selection(field_name, arguments: arguments).selected?
end