lib/graph_ql/parser/visitor.rb in graphql-0.1.0 vs lib/graph_ql/parser/visitor.rb in graphql-0.2.0
- old
+ new
@@ -7,41 +7,57 @@
# visitor[GraphQL::Nodes::Document].leave << -> (node) { p total_field_count }
# visitor.visit(document)
# # => 6
#
class GraphQL::Visitor
+ SKIP = :_skip
+
+ attr_reader :enter, :leave
def initialize
@visitors = {}
+ @enter = []
+ @leave = []
end
def [](node_class)
@visitors[node_class] ||= NodeVisitor.new
end
# Apply built-up vistors to `document`
- def visit(root)
- node_visitor = self[root.class]
- node_visitor.begin_visit(root)
- root.children.map { |child| visit(child) }
- node_visitor.end_visit(root)
+ def visit(root, parent=nil)
+ begin_visit(root, parent) &&
+ root.children.reduce(true) { |memo, child| memo && visit(child, root) }
+ end_visit(root, parent)
end
+ private
+
+ def begin_visit(node, parent)
+ self.class.apply_hooks(enter, node, parent)
+ node_visitor = self[node.class]
+ self.class.apply_hooks(node_visitor.enter, node, parent)
+ end
+
+ # Should global `leave` visitors come first or last?
+ def end_visit(node, parent)
+ self.class.apply_hooks(leave, node, parent)
+ node_visitor = self[node.class]
+ self.class.apply_hooks(node_visitor.leave, node, parent)
+ end
+
+ # If one of the visitors returns SKIP, stop visiting this node
+ def self.apply_hooks(hooks, node, parent)
+ hooks.reduce(true) { |memo, proc| memo && (proc.call(node, parent) != SKIP) }
+ end
+
class NodeVisitor
attr_reader :enter, :leave
def initialize
@enter = []
@leave = []
end
def <<(hook)
enter << hook
- end
-
- def begin_visit(node)
- enter.map{ |proc| proc.call(node) }
- end
-
- def end_visit(node)
- leave.map{ |proc| proc.call(node) }
end
end
end