Class: GraphQL::Language::Visitor

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/language/visitor.rb

Overview

Depth-first traversal through the tree, calling hooks at each stop.

Examples:

Create a visitor counting certain field names

class NameCounter < GraphQL::Language::Visitor
  def initialize(document, field_name)
    super(document)
    @field_name = field_name
    @count = 0
  end

  attr_reader :count

  def on_field(node, parent)
    # if this field matches our search, increment the counter
    if node.name == @field_name
      @count += 1
    end
    # Continue visiting subfields:
    super
  end
end

# Initialize a visitor
visitor = NameCounter.new(document, "name")
# Run it
visitor.visit
# Check the result
visitor.count
# => 3

Defined Under Namespace

Classes: DeleteNode, NodeVisitor

Constant Summary collapse

SKIP =
Deprecated.

Use super to continue the visit; or don't call it to halt.

If any hook returns this value, the GraphQL::Language::Visitor stops visiting this node right away

:_skip
DELETE_NODE =

When this is returned from a visitor method, Then the node passed into the method is removed from parent's children.

DeleteNode.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document) ⇒ Visitor

Returns a new instance of Visitor.


45
46
47
48
49
# File 'lib/graphql/language/visitor.rb', line 45

def initialize(document)
  @document = document
  @visitors = {}
  @result = nil
end

Instance Attribute Details

#resultGraphQL::Language::Nodes::Document (readonly)

Returns The document with any modifications applied.

Returns:


52
53
54
# File 'lib/graphql/language/visitor.rb', line 52

def result
  @result
end

Class Method Details

.make_visit_method(node_method) ⇒ Object

We don't use alias here because it breaks super


120
121
122
123
124
125
126
127
128
# File 'lib/graphql/language/visitor.rb', line 120

def self.make_visit_method(node_method)
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
    def #{node_method}(node, parent)
      child_mod = on_abstract_node(node, parent)
      # If visiting the children returned changes, continue passing those.
      child_mod || [node, parent]
    end
  RUBY
end

Instance Method Details

#[](node_class) ⇒ NodeVisitor

Deprecated.

see on_ methods, like #on_field

Get a NodeVisitor for node_class

Examples:

Run a hook whenever you enter a new Field

visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) { p "Here's a field" }

Parameters:

  • node_class (Class)

    The node class that you want to listen to

Returns:


61
62
63
# File 'lib/graphql/language/visitor.rb', line 61

def [](node_class)
  @visitors[node_class] ||= NodeVisitor.new
end

#on_abstract_node(node, parent) ⇒ Array?

The default implementation for visiting an AST node. It doesn't do anything, but it continues to visiting the node's children. To customize this hook, override one of its make_visit_methodes (or the base method?) in your subclasses.

For compatibility, it calls hook procs, too.

Parameters:

Returns:

  • (Array, nil)

    If there were modifications, it returns an array of new nodes, otherwise, it returns nil.


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/graphql/language/visitor.rb', line 91

def on_abstract_node(node, parent)
  if node == DELETE_NODE
    # This might be passed to `super(DELETE_NODE, ...)`
    # by a user hook, don't want to keep visiting in that case.
    nil
  else
    # Run hooks if there are any
    new_node = node
    no_hooks = !@visitors.key?(node.class)
    if no_hooks || begin_visit(new_node, parent)
      node.children.each do |child_node|
        new_child_and_node = on_node_with_modifications(child_node, new_node)
        # Reassign `node` in case the child hook makes a modification
        if new_child_and_node.is_a?(Array)
          new_node = new_child_and_node[1]
        end
      end
    end
    end_visit(new_node, parent) unless no_hooks

    if new_node.equal?(node)
      nil
    else
      [new_node, parent]
    end
  end
end

#visitvoid

This method returns an undefined value.

Visit document and all children, applying hooks as you go


67
68
69
70
71
72
73
74
75
# File 'lib/graphql/language/visitor.rb', line 67

def visit
  result = on_node_with_modifications(@document, nil)
  @result = if result.is_a?(Array)
    result.first
  else
    # The node wasn't modified
    @document
  end
end

#visit_node(node, parent) ⇒ Object

Call the user-defined handler for node.


78
79
80
# File 'lib/graphql/language/visitor.rb', line 78

def visit_node(node, parent)
  public_send(node.visit_method, node, parent)
end