class GraphQL::Language::Visitor
# => 3
visitor.count
# Check the result
visitor.visit
# Run it
visitor = NameCounter.new(document, “name”)
# Initialize a visitor
end
end
super
# Continue visiting subfields:
end
@count += 1
if node.name == @field_name
# if this field matches our search, increment the counter
def on_field(node, parent)
attr_reader :count
end
@count = 0
@field_name = field_name
super(document)
def initialize(document, field_name)
class NameCounter < GraphQL::Language::Visitor
@example Create a visitor counting certain field names
Depth-first traversal through the tree, calling hooks at each stop.
def self.apply_hooks(hooks, node, parent)
def self.apply_hooks(hooks, node, parent) hooks.each do |proc| return false if proc.call(node, parent) == SKIP end true end
def self.make_visit_method(node_method)
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
def [](node_class)
- see `on_` methods, like {#on_field}
Other tags:
- Example: Run a hook whenever you enter a new Field -
Returns:
-
(NodeVisitor)
-
Parameters:
-
node_class
(Class
) -- The node class that you want to listen to
def [](node_class) @visitors[node_class] ||= NodeVisitor.new end
def begin_visit(node, parent)
def begin_visit(node, parent) node_visitor = self[node.class] self.class.apply_hooks(node_visitor.enter, node, parent) end
def end_visit(node, parent)
def end_visit(node, parent) node_visitor = self[node.class] self.class.apply_hooks(node_visitor.leave, node, parent) end
def initialize(document)
def initialize(document) @document = document @visitors = {} @result = nil end
def on_abstract_node(node, parent)
-
(Array, nil)
- If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
Parameters:
-
parent
(GraphQL::Language::Nodes::AbstractNode, nil
) -- the previously-visited node, or `nil` if this is the root node. -
node
(GraphQL::Language::Nodes::AbstractNode
) -- the node being visited
def on_abstract_node(node, parent) if node.equal?(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
def on_node_with_modifications(node, parent)
If a non-array value is returned, consuming functions should ignore
then return the copies
copy `parent` so that it contains the copy of that node as a child,
Run the hooks for `node`, and if the hooks return a copy of `node`,
def on_node_with_modifications(node, parent) new_node_and_new_parent = visit_node(node, parent) if new_node_and_new_parent.is_a?(Array) new_node = new_node_and_new_parent[0] new_parent = new_node_and_new_parent[1] if new_node.is_a?(Nodes::AbstractNode) && !node.equal?(new_node) # The user-provided hook returned a new node. new_parent = new_parent && new_parent.replace_child(node, new_node) return new_node, new_parent elsif new_node.equal?(DELETE_NODE) # The user-provided hook requested to remove this node new_parent = new_parent && new_parent.delete_child(node) return nil, new_parent elsif new_node_and_new_parent.none? { |n| n == nil || n.class < Nodes::AbstractNode } # The user-provided hook returned an array of who-knows-what # return nil here to signify that no changes should be made nil else new_node_and_new_parent end else # The user-provided hook didn't make any modifications. # In fact, the hook might have returned who-knows-what, so # ignore the return value and use the original values. new_node_and_new_parent end end
def visit
-
(void)
-
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
def visit_node(node, parent)
def visit_node(node, parent) public_send(node.visit_method, node, parent) end