Class: GraphQL::Analysis::AST::Visitor
- Inherits:
 - 
      Language::Visitor
      
        
- Object
 - Language::Visitor
 - GraphQL::Analysis::AST::Visitor
 
 
- Defined in:
 - lib/graphql/analysis/ast/visitor.rb
 
Overview
Depth first traversal through a query AST, calling AST analyzers along the way.
The visitor is a special case of GraphQL::Language::Visitor, visiting only the selected operation, providing helpers for common use cases such as skipped fields and visiting fragment spreads.
Constant Summary
Constants inherited from Language::Visitor
Language::Visitor::DELETE_NODE, Language::Visitor::SKIP
Instance Attribute Summary collapse
- 
  
    
      #object_types  ⇒ Array<GraphQL::ObjectType> 
    
    
  
  
  
  
    
      readonly
    
    
  
  
  
  
  
  
    
Types whose scope we’ve entered.
 - 
  
    
      #query  ⇒ GraphQL::Query 
    
    
  
  
  
  
    
      readonly
    
    
  
  
  
  
  
  
    
The query being visited.
 
Attributes inherited from Language::Visitor
Instance Method Summary collapse
- 
  
    
      #argument_definition  ⇒ GraphQL::Argument? 
    
    
  
  
  
  
  
  
  
  
  
    
The most-recently-entered GraphQL::Argument, if currently inside one.
 - 
  
    
      #arguments_for(ast_node, field_definition)  ⇒ GraphQL::Query::Arguments 
    
    
  
  
  
  
  
  
  
  
  
    
Arguments for this node, merging default values, literal values and query variables.
 - 
  
    
      #directive_definition  ⇒ GraphQL::Directive? 
    
    
  
  
  
  
  
  
  
  
  
    
The most-recently-entered GraphQL::Directive, if currently inside one.
 - 
  
    
      #enter_fragment_spread_inline(fragment_spread)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
Visit a fragment spread inline instead of visiting the definition by itself.
 - 
  
    
      #field_definition  ⇒ GraphQL::Field? 
    
    
  
  
  
  
  
  
  
  
  
    
The most-recently-entered GraphQL::Field, if currently inside one.
 - 
  
    
      #initialize(query:, analyzers:)  ⇒ Visitor 
    
    
  
  
  
    constructor
  
  
  
  
  
  
  
    
A new instance of Visitor.
 - 
  
    
      #leave_fragment_spread_inline(_fragment_spread)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
Visit a fragment spread inline instead of visiting the definition by itself.
 - 
  
    
      #on_abstract_node(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_argument(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_directive(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_field(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_fragment_definition(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_fragment_spread(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_inline_fragment(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #on_operation_definition(node, parent)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
Visitor Hooks.
 - 
  
    
      #parent_type_definition  ⇒ GraphQL::BaseType 
    
    
  
  
  
  
  
  
  
  
  
    
The type which the current type came from.
 - 
  
    
      #previous_field_definition  ⇒ GraphQL::Field? 
    
    
  
  
  
  
  
  
  
  
  
    
The GraphQL field which returned the object that the current field belongs to.
 - 
  
    
      #response_path  ⇒ Array<String> 
    
    
  
  
  
  
  
  
  
  
  
    
The path to the response key for the current field.
 - 
  
    
      #skipping?  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    
If the current node should be skipped because of a skip or include directive.
 - 
  
    
      #type_definition  ⇒ GraphQL::BaseType 
    
    
  
  
  
  
  
  
  
  
  
    
The current object type.
 - 
  
    
      #visit  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    
 - 
  
    
      #visiting_fragment_definition?  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    
If the visitor is currently inside a fragment definition.
 
Methods inherited from Language::Visitor
#[], make_visit_method, #visit_node
Constructor Details
#initialize(query:, analyzers:) ⇒ Visitor
Returns a new instance of Visitor
      14 15 16 17 18 19 20 21 22 23 24 25 26 27  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 14 def initialize(query:, analyzers:) @analyzers = analyzers @path = [] @object_types = [] @directives = [] @field_definitions = [] @argument_definitions = [] @directive_definitions = [] @query = query @schema = query.schema @response_path = [] @skip_stack = [false] super(query.selected_operation) end  | 
  
Instance Attribute Details
#object_types ⇒ Array<GraphQL::ObjectType> (readonly)
Returns Types whose scope we’ve entered
      33 34 35  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 33 def object_types @object_types end  | 
  
#query ⇒ GraphQL::Query (readonly)
Returns the query being visited
      30 31 32  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 30 def query @query end  | 
  
Instance Method Details
#argument_definition ⇒ GraphQL::Argument?
Returns The most-recently-entered GraphQL::Argument, if currently inside one
      222 223 224 225 226  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 222 def argument_definition # Don't get the _last_ one because that's the current one. # Get the second-to-last one, which is the parent of the current one. @argument_definitions[-2] end  | 
  
#arguments_for(ast_node, field_definition) ⇒ GraphQL::Query::Arguments
Returns Arguments for this node, merging default values, literal values and query variables
      44 45 46  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 44 def arguments_for(ast_node, field_definition) @query.arguments_for(ast_node, field_definition) end  | 
  
#directive_definition ⇒ GraphQL::Directive?
Returns The most-recently-entered GraphQL::Directive, if currently inside one
      217 218 219  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 217 def directive_definition @directive_definitions.last end  | 
  
#enter_fragment_spread_inline(fragment_spread) ⇒ Object
Visit a fragment spread inline instead of visiting the definition by itself.
      174 175 176 177 178 179 180 181 182 183 184 185 186 187 188  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 174 def enter_fragment_spread_inline(fragment_spread) fragment_def = query.fragments[fragment_spread.name] object_type = if fragment_def.type query.schema.types.fetch(fragment_def.type.name, nil) else object_types.last end object_types << object_type fragment_def.selections.each do |selection| visit_node(selection, fragment_def) end end  | 
  
#field_definition ⇒ GraphQL::Field?
Returns The most-recently-entered GraphQL::Field, if currently inside one
      207 208 209  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 207 def field_definition @field_definitions.last end  | 
  
#leave_fragment_spread_inline(_fragment_spread) ⇒ Object
Visit a fragment spread inline instead of visiting the definition by itself.
      192 193 194  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 192 def leave_fragment_spread_inline(_fragment_spread) object_types.pop end  | 
  
#on_abstract_node(node, parent) ⇒ Object
      166 167 168 169 170  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 166 def on_abstract_node(node, parent) call_analyzers(:on_enter_abstract_node, node, parent) super call_analyzers(:on_leave_abstract_node, node, parent) end  | 
  
#on_argument(node, parent) ⇒ Object
      133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 133 def on_argument(node, parent) argument_defn = if (arg = @argument_definitions.last) arg_type = arg.type.unwrap if arg_type.kind.input_object? arg_type.input_fields[node.name] else nil end elsif (directive_defn = @directive_definitions.last) directive_defn.arguments[node.name] elsif (field_defn = @field_definitions.last) field_defn.arguments[node.name] else nil end @argument_definitions.push(argument_defn) @path.push(node.name) call_analyzers(:on_enter_argument, node, parent) super call_analyzers(:on_leave_argument, node, parent) @argument_definitions.pop @path.pop end  | 
  
#on_directive(node, parent) ⇒ Object
      124 125 126 127 128 129 130 131  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 124 def on_directive(node, parent) directive_defn = @schema.directives[node.name] @directive_definitions.push(directive_defn) call_analyzers(:on_enter_directive, node, parent) super call_analyzers(:on_leave_directive, node, parent) @directive_definitions.pop end  | 
  
#on_field(node, parent) ⇒ Object
      96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 96 def on_field(node, parent) @response_path.push(node.alias || node.name) parent_type = @object_types.last field_definition = @schema.get_field(parent_type, node.name) @field_definitions.push(field_definition) if !field_definition.nil? next_object_type = field_definition.type.unwrap @object_types.push(next_object_type) else @object_types.push(nil) end @path.push(node.alias || node.name) @skipping = @skip_stack.last || skip?(node) @skip_stack << @skipping call_analyzers(:on_enter_field, node, parent) super @skipping = @skip_stack.pop call_analyzers(:on_leave_field, node, parent) @response_path.pop @field_definitions.pop @object_types.pop @path.pop end  | 
  
#on_fragment_definition(node, parent) ⇒ Object
      76 77 78 79 80 81 82 83 84 85  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 76 def on_fragment_definition(node, parent) on_fragment_with_type(node) do @path.push("fragment #{node.name}") @in_fragment_def = false call_analyzers(:on_enter_fragment_definition, node, parent) super @in_fragment_def = false call_analyzers(:on_leave_fragment_definition, node, parent) end end  | 
  
#on_fragment_spread(node, parent) ⇒ Object
      158 159 160 161 162 163 164  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 158 def on_fragment_spread(node, parent) @path.push("... #{node.name}") call_analyzers(:on_enter_fragment_spread, node, parent) super call_analyzers(:on_leave_fragment_spread, node, parent) @path.pop end  | 
  
#on_inline_fragment(node, parent) ⇒ Object
      87 88 89 90 91 92 93 94  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 87 def on_inline_fragment(node, parent) on_fragment_with_type(node) do @path.push("...#{node.type ? " on #{node.type.name}" : ""}") call_analyzers(:on_enter_inline_fragment, node, parent) super call_analyzers(:on_leave_inline_fragment, node, parent) end end  | 
  
#on_operation_definition(node, parent) ⇒ Object
Visitor Hooks
      65 66 67 68 69 70 71 72 73 74  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 65 def on_operation_definition(node, parent) object_type = @schema.root_type_for_operation(node.operation_type) @object_types.push(object_type) @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}") call_analyzers(:on_enter_operation_definition, node, parent) super call_analyzers(:on_leave_operation_definition, node, parent) @object_types.pop @path.pop end  | 
  
#parent_type_definition ⇒ GraphQL::BaseType
Returns The type which the current type came from
      202 203 204  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 202 def parent_type_definition @object_types[-2] end  | 
  
#previous_field_definition ⇒ GraphQL::Field?
Returns The GraphQL field which returned the object that the current field belongs to
      212 213 214  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 212 def previous_field_definition @field_definitions[-2] end  | 
  
#response_path ⇒ Array<String>
Returns The path to the response key for the current field
      59 60 61  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 59 def response_path @response_path.dup end  | 
  
#skipping? ⇒ Boolean
Returns If the current node should be skipped because of a skip or include directive
      54 55 56  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 54 def skipping? @skipping end  | 
  
#type_definition ⇒ GraphQL::BaseType
Returns The current object type
      197 198 199  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 197 def type_definition @object_types.last end  | 
  
#visit ⇒ Object
      35 36 37 38  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 35 def visit return unless @document super end  | 
  
#visiting_fragment_definition? ⇒ Boolean
Returns If the visitor is currently inside a fragment definition
      49 50 51  | 
    
      # File 'lib/graphql/analysis/ast/visitor.rb', line 49 def visiting_fragment_definition? @in_fragment_def end  |