lib/jinx/helpers/visitor.rb in jinx-2.1.2 vs lib/jinx/helpers/visitor.rb in jinx-2.1.3
- old
+ new
@@ -191,11 +191,11 @@
# filter.to_enum(joe) { |person| person.age } #=> [55, 24, 3, 6]
#
# @return [Visitor] the filter visitor
# @yield [parent, children] the filter to select which of the children to visit next
# @yieldparam parent the currently visited node
- # @yieldparam children the nodes slated by this Visitor to visit next
+ # @yieldparam [Array] children the nodes slated by this visitor to visit next
# @raise [ArgumentError] if a block is not given to this method
def filter
raise ArgumentError.new("A filter block is not given to the visitor filter method") unless block_given?
Visitor.new(@options) { |node| yield(node, node_children(node)) }
end
@@ -227,24 +227,32 @@
end
# Visits the root node and all descendants.
def visit_root(node, &operator)
clear
- prune_cycle_nodes(node) if @prune_cycle_flag
- # visit the root node
- visit_recursive(node, &operator)
+ # Exclude cycles if the prune cycles flag is set.
+ @exclude.merge!(cyclic_nodes(node)) if @prune_cycle_flag
+ # Visit the root node.
+ result = visit_recursive(node, &operator)
+ # Reset the exclusions if the prune cycles flag is set.
+ @exclude.clear if @prune_cycle_flag
+ result
end
-
- # Excludes the internal nodes in cycles starting and ending at the given root.
- def prune_cycle_nodes(root)
- @exclude.clear
- # visit the root, which will detect cycles, and remove the visited nodes afterwords
- @prune_cycle_flag = false
- to_enum(root).collect.each { |node| @visited.delete(node) }
- @prune_cycle_flag = true
- # add each cyclic internal node to the exclude list
- @cycles.each { |cycle| cycle[1...-1].each { |node| @exclude << node } if cycle.first == root }
+
+ # @example
+ # visitor.visit(a)
+ # visit.to_enum #=> [a, b, c, u, v, x, y, z]
+ # visit.cycles #=> [[a, b, u, x, a], [c, z, c]]
+ # visit.cyclic_nodes #=> [b, u, x, c, z]
+ # @return [Array] the non-root nodes in visit cycles
+ def cyclic_nodes(root)
+ copts = @options.reject { |k, v| k == :prune_cycle }
+ cycler = Visitor.new(copts, &@navigator)
+ cycler.visit(root)
+ cyclic = cycler.cycles.flatten.uniq
+ cyclic.delete(root)
+ cyclic
end
def visit_recursive(node, &operator)
# bail if no node or the node is specifically excluded
return if node.nil? or @exclude.include?(node)
@@ -296,11 +304,13 @@
def initialize(visitor, node)
@visitor = visitor
@root = node
end
-
+
+ # @yield [node] operates on the visited node
+ # @yieldparam node the visited node
def each
@visitor.visit(@root) { |node| yield(node) }
end
end
@@ -312,23 +322,23 @@
super() { |nodes| match_children(visitor, nodes, &matcher) }
end
# Visits the given pair of nodes.
#
- # Raises ArgumentError if nodes does not consist of either two node arguments or one two-item Array
- # argument.
+ # @param [(Object, Object), <(Object, Object)>] nodes the node pair
+ # @raise [ArgumentError] if the arguments do not consist of either two nodes or one two-item array
def visit(*nodes)
if nodes.size == 1 then
nodes = nodes.first
raise ArgumentError.new("Sync visitor requires a pair of entry nodes") unless nodes.size == 2
end
super(nodes)
end
- # Returns an Enumerable which applies the given block to each matched node starting at the given nodes.
- #
- # Raises ArgumentError if nodes does not consist of either two node arguments or one two-item Array
- # argument.
+ # @param (see #visit)
+ # @param [(Object, Object), <(Object, Object)>] nodes
+ # @return [Enumerable] the result of applying the given block to each matched node starting at the given root nodes
+ # @raise [ArgumentError] if the arguments do not consist of either two nodes or one two-item array
def to_enum(*nodes)
if nodes.size == 1 then
nodes = nodes.first
raise ArgumentError.new("Sync visitor requires a pair of entry nodes") unless nodes.size == 2
end