From 98984de5406be6d562144b8742d63f03fdaf831a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 5 Sep 2014 19:42:38 +0200 Subject: [PATCH] Moved Document#each_node into a separate module. This allows it to be re-used by XML::Node. --- lib/oga.rb | 1 + lib/oga/xml/document.rb | 41 +---------------------------- lib/oga/xml/node.rb | 2 ++ lib/oga/xml/traversal.rb | 48 ++++++++++++++++++++++++++++++++++ spec/oga/xml/document_spec.rb | 41 ----------------------------- spec/oga/xml/traversal_spec.rb | 44 +++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 81 deletions(-) create mode 100644 lib/oga/xml/traversal.rb create mode 100644 spec/oga/xml/traversal_spec.rb diff --git a/lib/oga.rb b/lib/oga.rb index 8498725..aadd7fc 100644 --- a/lib/oga.rb +++ b/lib/oga.rb @@ -20,6 +20,7 @@ end #:nocov: require_relative 'oga/xml/querying' +require_relative 'oga/xml/traversal' require_relative 'oga/xml/node' require_relative 'oga/xml/document' require_relative 'oga/xml/character_node' diff --git a/lib/oga/xml/document.rb b/lib/oga/xml/document.rb index 2c9fb33..1a56c39 100644 --- a/lib/oga/xml/document.rb +++ b/lib/oga/xml/document.rb @@ -14,6 +14,7 @@ module Oga # class Document include Querying + include Traversal attr_accessor :doctype, :xml_declaration @@ -51,46 +52,6 @@ module Oga 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. # diff --git a/lib/oga/xml/node.rb b/lib/oga/xml/node.rb index 0cabf6d..1d6d76b 100644 --- a/lib/oga/xml/node.rb +++ b/lib/oga/xml/node.rb @@ -9,6 +9,8 @@ module Oga # @return [Oga::XML::NodeSet] # class Node + include Traversal + attr_accessor :node_set ## diff --git a/lib/oga/xml/traversal.rb b/lib/oga/xml/traversal.rb new file mode 100644 index 0000000..4903e07 --- /dev/null +++ b/lib/oga/xml/traversal.rb @@ -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 diff --git a/spec/oga/xml/document_spec.rb b/spec/oga/xml/document_spec.rb index 5c68e5c..62df667 100644 --- a/spec/oga/xml/document_spec.rb +++ b/spec/oga/xml/document_spec.rb @@ -30,47 +30,6 @@ describe Oga::XML::Document do end end - context '#each_node' do - before do - @document = parse(<<-EOF.strip.gsub(/\s+/m, '')) - - - Foo - - - Bar - - - 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 before do child = Oga::XML::Comment.new(:text => 'foo') diff --git a/spec/oga/xml/traversal_spec.rb b/spec/oga/xml/traversal_spec.rb new file mode 100644 index 0000000..23b16c9 --- /dev/null +++ b/spec/oga/xml/traversal_spec.rb @@ -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, '')) + + + Foo + + + Bar + + + 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