diff --git a/lib/oga.rb b/lib/oga.rb index b09e12c..9a12daf 100644 --- a/lib/oga.rb +++ b/lib/oga.rb @@ -23,6 +23,7 @@ require_relative 'oga/xml/comment' require_relative 'oga/xml/cdata' require_relative 'oga/xml/xml_declaration' require_relative 'oga/xml/doctype' +require_relative 'oga/xml/node_set' require_relative 'oga/html/parser' diff --git a/lib/oga/xml/element.rb b/lib/oga/xml/element.rb index 10b30f2..aaf9af9 100644 --- a/lib/oga/xml/element.rb +++ b/lib/oga/xml/element.rb @@ -46,6 +46,15 @@ module Oga alias_method :attr, :attribute + ## + # Returns the text of all child nodes joined together. + # + # @return [String] + # + def text + return children.text + end + ## # Converts the element and its child elements to XML. # diff --git a/lib/oga/xml/node.rb b/lib/oga/xml/node.rb index 5e20db1..a1a9d85 100644 --- a/lib/oga/xml/node.rb +++ b/lib/oga/xml/node.rb @@ -16,8 +16,11 @@ module Oga # @!attribute [rw] previous # @return [Oga::XML::Node] # + # @!attribute [rw] node_set + # @return [Oga::XML::NodeSet] + # class Node - attr_accessor :parent, :children, :next, :previous + attr_accessor :parent, :children, :next, :previous, :node_set ## # @param [Hash] options diff --git a/lib/oga/xml/node_set.rb b/lib/oga/xml/node_set.rb index 860e5ea..333944e 100644 --- a/lib/oga/xml/node_set.rb +++ b/lib/oga/xml/node_set.rb @@ -15,10 +15,19 @@ module Oga @nodes.each { |node| yield node } end + def last + return @nodes[-1] + end + + def empty? + return @nodes.empty? + end + def length return @nodes.length end + alias_method :count, :length alias_method :size, :length def index(node) @@ -36,7 +45,7 @@ module Oga end def shift - return @noes.shift + return @nodes.shift end def pop @@ -61,7 +70,7 @@ module Oga values = [] @nodes.each do |node| - if node.node_type == :element + if node.respond_to?(:attribute) values << node.attribute(name) end end @@ -75,14 +84,16 @@ module Oga text = '' @nodes.each do |node| - text << node.text + if node.respond_to?(:text) + text << node.text + end end return text end def associate_nodes! - @nodes.each_with_index do |node, index| + @nodes.each do |node| node.node_set = self end end diff --git a/spec/oga/xml/node_set_spec.rb b/spec/oga/xml/node_set_spec.rb new file mode 100644 index 0000000..9ebc66b --- /dev/null +++ b/spec/oga/xml/node_set_spec.rb @@ -0,0 +1,221 @@ +require 'spec_helper' + +describe Oga::XML::NodeSet do + context '#initialize' do + example 'create an empty node set' do + described_class.new.length.should == 0 + end + + example 'create a node set with a single node' do + node = Oga::XML::Element.new(:name => 'p') + + described_class.new([node]).length.should == 1 + end + end + + context '#each' do + example 'yield the block for every node' do + n1 = Oga::XML::Element.new(:name => 'a') + n2 = Oga::XML::Element.new(:name => 'b') + + set = described_class.new([n1, n2]) + yielded = [] + + set.each { |node| yielded << node } + + yielded.should == [n1, n2] + end + end + + context 'Enumerable behaviour' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @n2 = Oga::XML::Element.new(:name => 'b') + @set = described_class.new([@n1, @n2]) + end + + example 'return the first node' do + @set.first.should == @n1 + end + + example 'return the last node' do + @set.last.should == @n2 + end + + example 'return the amount of nodes' do + @set.count.should == 2 + @set.length.should == 2 + @set.size.should == 2 + end + + example 'return a boolean that indicates if a set is empty or not' do + @set.empty?.should == false + end + end + + context '#index' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @n2 = Oga::XML::Element.new(:name => 'b') + @set = described_class.new([@n1, @n2]) + end + + example 'return the index of the first node' do + @set.index(@n1).should == 0 + end + + example 'return the index of the last node' do + @set.index(@n2).should == 1 + end + end + + context '#push' do + before do + @set = described_class.new + end + + example 'push a node into the set' do + @set.push(Oga::XML::Element.new(:name => 'a')) + + @set.length.should == 1 + end + end + + context '#unshift' do + before do + n1 = Oga::XML::Element.new(:name => 'a') + @set = described_class.new([n1]) + end + + example 'push a node at the beginning of the set' do + n2 = Oga::XML::Element.new(:name => 'b') + + @set.unshift(n2) + + @set.first.should == n2 + end + end + + context '#shift' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @set = described_class.new([@n1]) + end + + example 'remove the node from the set' do + @set.shift + @set.empty?.should == true + end + + example 'return the node when shifting it' do + @set.shift.should == @n1 + end + end + + context '#pop' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @set = described_class.new([@n1]) + end + + example 'remove the node from the set' do + @set.pop + @set.empty?.should == true + end + + example 'return the node when popping it' do + @set.pop.should == @n1 + end + end + + context '#remove' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @n2 = Oga::XML::Element.new(:name => 'b') + + @doc_set = described_class.new([@n1, @n2]) + @query_set = described_class.new([@n1, @n2]) + + @doc_set.associate_nodes! + end + + example 'do not remove the nodes from the current set' do + @query_set.remove + + @query_set.empty?.should == false + end + + example 'remove the nodes from the owning set' do + @query_set.remove + + @doc_set.empty?.should == true + end + + example 'unlink the nodes from the sets they belong to' do + @query_set.remove + + @n1.node_set.nil?.should == true + @n2.node_set.nil?.should == true + end + end + + context '#delete' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @set = described_class.new([@n1]) + end + + example 'return the node when deleting it' do + @set.delete(@n1).should == @n1 + end + + example 'remove the node from the set' do + @set.delete(@n1) + + @set.empty?.should == true + end + end + + context '#attribute' do + before do + @el = Oga::XML::Element.new(:name => 'a', :attributes => {:a => '1'}) + @txt = Oga::XML::Text.new(:text => 'foo') + @set = described_class.new([@el, @txt]) + end + + example 'return the values of an attribute' do + @set.attribute('a').should == ['1'] + end + end + + context '#text' do + before do + child = Oga::XML::Text.new(:text => 'foo') + + @el = Oga::XML::Element.new( + :name => 'a', + :children => described_class.new([child]) + ) + + @txt = Oga::XML::Text.new(:text => "\nbar") + @set = described_class.new([@el, @txt]) + end + + example 'return the text of all nodes' do + @set.text.should == "foo\nbar" + end + end + + context '#associate_nodes!' do + before do + @n1 = Oga::XML::Element.new(:name => 'a') + @set = described_class.new([@n1]) + end + + example 'associate a node with a set' do + @set.associate_nodes! + + @n1.node_set.should == @set + end + end +end