Moved Document#each_node into a separate module.

This allows it to be re-used by XML::Node.
This commit is contained in:
Yorick Peterse 2014-09-05 19:42:38 +02:00
parent 8f562c24dd
commit 98984de540
6 changed files with 96 additions and 81 deletions

View File

@ -20,6 +20,7 @@ end
#:nocov: #:nocov:
require_relative 'oga/xml/querying' require_relative 'oga/xml/querying'
require_relative 'oga/xml/traversal'
require_relative 'oga/xml/node' require_relative 'oga/xml/node'
require_relative 'oga/xml/document' require_relative 'oga/xml/document'
require_relative 'oga/xml/character_node' require_relative 'oga/xml/character_node'

View File

@ -14,6 +14,7 @@ module Oga
# #
class Document class Document
include Querying include Querying
include Traversal
attr_accessor :doctype, :xml_declaration attr_accessor :doctype, :xml_declaration
@ -51,46 +52,6 @@ module Oga
end end
end end
##
# Traverses through the document and yields every node to the supplied
# block.
#
# The block's body can also determine whether or not to traverse child
# nodes. Preventing a node's children from being traversed can be done by
# using `throw :skip_children`
#
# This method uses a combination of breadth-first and depth-first
# traversal to traverse the entire XML tree in document order. See
# http://en.wikipedia.org/wiki/Breadth-first_search for more information.
#
# @example
# document.each_node do |node|
# p node.class
# end
#
# @example Skipping the children of a certain node
# document.each_node do |node|
# if node.is_a?(Oga::XML::Element) and node.name == 'book'
# throw :skip_children
# end
# end
#
# @yieldparam [Oga::XML::Node] The current node.
#
def each_node
visit = children.to_a.dup # copy it since we're modifying the array
until visit.empty?
current = visit.shift
catch :skip_children do
yield current
visit = current.children.to_a + visit
end
end
end
## ##
# Converts the document and its child nodes to XML. # Converts the document and its child nodes to XML.
# #

View File

@ -9,6 +9,8 @@ module Oga
# @return [Oga::XML::NodeSet] # @return [Oga::XML::NodeSet]
# #
class Node class Node
include Traversal
attr_accessor :node_set attr_accessor :node_set
## ##

48
lib/oga/xml/traversal.rb Normal file
View File

@ -0,0 +1,48 @@
module Oga
module XML
##
# Module that provides methods to traverse DOM trees.
#
module Traversal
##
# Traverses through the node and yields every child node to the supplied
# block.
#
# The block's body can also determine whether or not to traverse child
# nodes. Preventing a node's children from being traversed can be done by
# using `throw :skip_children`
#
# This method uses a combination of breadth-first and depth-first
# traversal to traverse the entire XML tree in document order. See
# http://en.wikipedia.org/wiki/Breadth-first_search for more information.
#
# @example
# document.each_node do |node|
# p node.class
# end
#
# @example Skipping the children of a certain node
# document.each_node do |node|
# if node.is_a?(Oga::XML::Element) and node.name == 'book'
# throw :skip_children
# end
# end
#
# @yieldparam [Oga::XML::Node] The current node.
#
def each_node
visit = children.to_a.dup # copy it since we're modifying the array
until visit.empty?
current = visit.shift
catch :skip_children do
yield current
visit = current.children.to_a + visit
end
end
end
end # Traversal
end # XML
end # Oga

View File

@ -30,47 +30,6 @@ describe Oga::XML::Document do
end end
end end
context '#each_node' do
before do
@document = parse(<<-EOF.strip.gsub(/\s+/m, ''))
<books>
<book1>
<title1>Foo</title1>
</book1>
<book2>
<title2>Bar</title2>
</book2>
</books>
EOF
end
example 'yield the nodes in document order' do
names = []
@document.each_node do |node|
names << (node.is_a?(Oga::XML::Element) ? node.name : node.text)
end
names.should == %w{books book1 title1 Foo book2 title2 Bar}
end
example 'skip child nodes when skip_children is thrown' do
names = []
@document.each_node do |node|
if node.is_a?(Oga::XML::Element)
if node.name == 'book1'
throw :skip_children
else
names << node.name
end
end
end
names.should == %w{books book2 title2}
end
end
context '#to_xml' do context '#to_xml' do
before do before do
child = Oga::XML::Comment.new(:text => 'foo') child = Oga::XML::Comment.new(:text => 'foo')

View File

@ -0,0 +1,44 @@
require 'spec_helper'
describe Oga::XML::Traversal do
context '#each_node' do
before do
@document = parse(<<-EOF.strip.gsub(/\s+/m, ''))
<books>
<book1>
<title1>Foo</title1>
</book1>
<book2>
<title2>Bar</title2>
</book2>
</books>
EOF
end
example 'yield the nodes in document order' do
names = []
@document.each_node do |node|
names << (node.is_a?(Oga::XML::Element) ? node.name : node.text)
end
names.should == %w{books book1 title1 Foo book2 title2 Bar}
end
example 'skip child nodes when skip_children is thrown' do
names = []
@document.each_node do |node|
if node.is_a?(Oga::XML::Element)
if node.name == 'book1'
throw :skip_children
else
names << node.name
end
end
end
names.should == %w{books book2 title2}
end
end
end