Tests + docs for the TreeBuilder class.
This commit is contained in:
parent
6d866523b8
commit
f99c13b516
|
@ -1,14 +1,21 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class for building a DOM tree of XML/HTML nodes.
|
||||
# The TreeBuilder class turns an AST into a DOM tree. This DOM tree can be
|
||||
# traversed by requesting child elements, parent elements, etc.
|
||||
#
|
||||
# @!attribute [r] ast
|
||||
# @return [Oga::AST::Node]
|
||||
# Basic usage:
|
||||
#
|
||||
# builder = Oga::XML::TreeBuilder.new
|
||||
# ast = s(:element, ...)
|
||||
#
|
||||
# builder.process(ast) # => #<Oga::XML::Document ...>
|
||||
#
|
||||
class TreeBuilder < ::AST::Processor
|
||||
attr_reader :ast
|
||||
|
||||
##
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Oga::XML::Document]
|
||||
#
|
||||
def on_document(node)
|
||||
document = Document.new
|
||||
document.children = process_all(node)
|
||||
|
@ -20,10 +27,23 @@ module Oga
|
|||
return document
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Oga::XML::Comment]
|
||||
#
|
||||
def on_comment(node)
|
||||
return Comment.new(:text => node.children[0])
|
||||
end
|
||||
|
||||
##
|
||||
# Processes an `(element)` node and its child elements.
|
||||
#
|
||||
# An element can have a parent, previous and next element as well as a
|
||||
# number of child elements.
|
||||
#
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Oga::XML::Element]
|
||||
#
|
||||
def on_element(node)
|
||||
ns, name, attr, *children = *node
|
||||
|
||||
|
@ -32,7 +52,7 @@ module Oga
|
|||
end
|
||||
|
||||
if children
|
||||
children = process_all(children)
|
||||
children = process_all(children.compact)
|
||||
end
|
||||
|
||||
element = Element.new(
|
||||
|
@ -42,38 +62,73 @@ module Oga
|
|||
:children => children
|
||||
)
|
||||
|
||||
element.children.each_with_index do |child, index|
|
||||
if index > 0
|
||||
child.previous = element.children[index - 1]
|
||||
end
|
||||
|
||||
if index + 1 <= element.children.length
|
||||
child.next = element.children[index + 1]
|
||||
end
|
||||
|
||||
child.parent = element
|
||||
end
|
||||
process_children(element)
|
||||
|
||||
return element
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Oga::XML::Text]
|
||||
#
|
||||
def on_text(node)
|
||||
return Text.new(:text => node.children[0])
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Oga::XML::Cdata]
|
||||
#
|
||||
def on_cdata(node)
|
||||
return Cdata.new(:text => node.children[0])
|
||||
end
|
||||
|
||||
##
|
||||
# Converts a `(attributes)` node into a Hash.
|
||||
#
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Hash]
|
||||
#
|
||||
def on_attributes(node)
|
||||
pairs = process_all(node)
|
||||
|
||||
return Hash[pairs]
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::AST::Node] node
|
||||
# @return [Array]
|
||||
#
|
||||
def on_attribute(node)
|
||||
return *node
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Iterates over the child elements of an element and assigns the parent,
|
||||
# previous and next elements. The supplied object is modified in place.
|
||||
#
|
||||
# @param [Oga::XML::Element] element
|
||||
#
|
||||
def process_children(element)
|
||||
amount = element.children.length
|
||||
|
||||
element.children.each_with_index do |child, index|
|
||||
prev_index = index - 1
|
||||
next_index = index + 1
|
||||
|
||||
if index > 0
|
||||
child.previous = element.children[prev_index]
|
||||
end
|
||||
|
||||
if next_index <= amount
|
||||
child.next = element.children[next_index]
|
||||
end
|
||||
|
||||
child.parent = element
|
||||
end
|
||||
end
|
||||
end # TreeBuilder
|
||||
end # XML
|
||||
end # Oga
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Oga::XML::TreeBuilder do
|
||||
before do
|
||||
@builder = described_class.new
|
||||
end
|
||||
|
||||
context '#on_element' do
|
||||
context 'simple elements' do
|
||||
before do
|
||||
node = s(:element, 'foo', 'p', nil, nil)
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'return a Element node' do
|
||||
@tag.is_a?(Oga::XML::Element).should == true
|
||||
end
|
||||
|
||||
example 'include the name of the element' do
|
||||
@tag.name.should == 'p'
|
||||
end
|
||||
|
||||
example 'include the namespace of the element' do
|
||||
@tag.namespace.should == 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
context 'elements with attributes' do
|
||||
before do
|
||||
node = s(
|
||||
:element,
|
||||
nil,
|
||||
'p',
|
||||
s(:attributes, s(:attribute, 'key', 'value')),
|
||||
nil
|
||||
)
|
||||
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'include the name of the element' do
|
||||
@tag.name.should == 'p'
|
||||
end
|
||||
|
||||
example 'include the attributes' do
|
||||
@tag.attributes.should == {'key' => 'value'}
|
||||
end
|
||||
end
|
||||
|
||||
context 'elements with parent elements' do
|
||||
before do
|
||||
node = s(:element, nil, 'p', nil, s(:element, nil, 'span', nil, nil))
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'set the parent element' do
|
||||
@tag.children[0].parent.should == @tag
|
||||
end
|
||||
end
|
||||
|
||||
context 'elements with next elements' do
|
||||
before do
|
||||
node = s(
|
||||
:element,
|
||||
nil,
|
||||
'p',
|
||||
nil,
|
||||
s(:element, nil, 'a', nil, nil),
|
||||
s(:element, nil, 'span', nil, nil)
|
||||
)
|
||||
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'set the next element' do
|
||||
@tag.children[0].next.should == @tag.children[1]
|
||||
end
|
||||
|
||||
example 'do not set the next element for the last element' do
|
||||
@tag.children[1].next.should == nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'elements with previous elements' do
|
||||
before do
|
||||
node = s(
|
||||
:element,
|
||||
nil,
|
||||
'p',
|
||||
nil,
|
||||
s(:element, nil, 'a', nil, nil),
|
||||
s(:element, nil, 'span', nil, nil)
|
||||
)
|
||||
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'set the previous element' do
|
||||
@tag.children[1].previous.should == @tag.children[0]
|
||||
end
|
||||
|
||||
example 'do not set the previous element for the first element' do
|
||||
@tag.children[0].previous.should == nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'elements with child elements' do
|
||||
before do
|
||||
node = s(:element, nil, 'p', nil, s(:element, nil, 'span', nil, nil))
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'include the name of the element' do
|
||||
@tag.name.should == 'p'
|
||||
end
|
||||
|
||||
example 'include the child element' do
|
||||
@tag.children[0].is_a?(Oga::XML::Element).should == true
|
||||
end
|
||||
|
||||
example 'include the name of the child element' do
|
||||
@tag.children[0].name.should == 'span'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#on_text' do
|
||||
before do
|
||||
node = s(:text, 'Hello')
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'return a Text node' do
|
||||
@tag.is_a?(Oga::XML::Text).should == true
|
||||
end
|
||||
|
||||
example 'include the text of the node' do
|
||||
@tag.text.should == 'Hello'
|
||||
end
|
||||
end
|
||||
|
||||
context '#on_cdata' do
|
||||
before do
|
||||
node = s(:cdata, 'Hello')
|
||||
@tag = @builder.process(node)
|
||||
end
|
||||
|
||||
example 'return a Cdata node' do
|
||||
@tag.is_a?(Oga::XML::Cdata).should == true
|
||||
end
|
||||
|
||||
example 'include the text of the node' do
|
||||
@tag.text.should == 'Hello'
|
||||
end
|
||||
end
|
||||
|
||||
context '#on_attributes' do
|
||||
before do
|
||||
@node = s(
|
||||
:attributes,
|
||||
s(:attribute, 'foo', 'bar'),
|
||||
s(:attribute, 'baz', 'wat')
|
||||
)
|
||||
end
|
||||
|
||||
example 'return the attributes as a Hash' do
|
||||
@builder.process(@node).should == {'foo' => 'bar', 'baz' => 'wat'}
|
||||
end
|
||||
|
||||
example 'return an empty Hash by default' do
|
||||
@builder.process(s(:attributes)).should == {}
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue