Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 439362cd56393040a0cd9336ccde682d132db9dc 0 parents
@tobi authored
36 README
@@ -0,0 +1,36 @@
+XML Node
+==========
+
+
+Example for generating xml:
+
+ # Create an atom like document
+
+ root = XmlNode.new 'feed' do |feed|
+
+ feed << XmlNode.new('id', 'tag:atom.com,2007:1')
+ feed << XmlNode.new('title', 'Atom test feed')
+ feed << XmlNode.new('author') do |author|
+ author << XmlNode.new("name", "tobi")
+ author << XmlNode.new("email", "tobi@gmail.com")
+ end
+
+ feed << XmlNode.new('entry') do |entry|
+ entry << XmlNode.new('title', 'First post')
+ entry << XmlNode.new('summary', 'Lorem ipsum', :type => 'xhtml')
+ entry << XmlNode.new('created_at', Time.now)
+ end
+
+ feed << XmlNode.new('dc:published', Time.now)
+ end
+
+ root.to_xml #=> Well formatted xml
+
+
+Example for parsing xml:
+
+
+ xml = XmlNode.parse('<feed attr="1"><element>text</element><element>text</element></feed>')
+ xml['attr'] #=> '1'
+ xml.children['element'].text #=> 'text'
+ xml.children.each { |e| e... }
21 Rakefile
@@ -0,0 +1,21 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/test_*.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the calculations plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'XmlNode'
+ rdoc.options << '--line-numbers --inline-source'
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
32 benchmark/bench_generation.rb
@@ -0,0 +1,32 @@
+require "benchmark"
+require File.dirname(__FILE__) + "/../lib/xml_node"
+
+class XmlNode
+
+ def to_xml_as_array
+ xml = []
+ document = REXML::Document.new
+ document << REXML::XMLDecl.new('1.0')
+ document << @element
+ document.write( xml, 0)
+ xml.to_s
+ end
+
+ def to_xml_no_format
+ xml = ''
+ document = REXML::Document.new
+ document << REXML::XMLDecl.new('1.0')
+ document << @element
+ document.write( xml)
+ xml
+ end
+
+end
+
+TESTS = 10000
+
+Benchmark.bmbm do |results|
+ results.report { TESTS.times do XmlNode.new('feed') { |n| n << XmlNode.new('element', 'test'); n << XmlNode.new('element') }.to_xml end }
+ results.report { TESTS.times do XmlNode.new('feed') { |n| n << XmlNode.new('element', 'test'); n << XmlNode.new('element') }.to_xml_as_array end }
+ results.report { TESTS.times do XmlNode.new('feed') { |n| n << XmlNode.new('element', 'test'); n << XmlNode.new('element') }.to_xml_no_format end }
+end
1  init.rb
@@ -0,0 +1 @@
+# Include hook code here
220 lib/xml_node.rb
@@ -0,0 +1,220 @@
+require 'rexml/document'
+
+class Object
+ def to_xml_value
+ to_s
+ end
+end
+
+class NilClass
+ def to_xml_value
+ nil
+ end
+end
+
+class TrueClass
+ def to_xml_value
+ to_s
+ end
+end
+
+class FalseClass
+ def to_xml_value
+ to_s
+ end
+end
+
+class Time
+ def to_xml_value
+ self.xmlschema
+ end
+end
+
+class DateTime
+ def to_xml_value
+ self.xmlschema
+ end
+end
+
+class Date
+ def to_xml_value
+ self.to_time.xmlschema
+ end
+end
+
+class REXML::Element
+ def to_xml_element
+ self
+ end
+end
+
+class XmlNode
+ attr_accessor :child_nodes
+ attr_reader :element
+
+ class List
+ include Enumerable
+
+ def initialize(parent)
+ @parent = parent
+ @children = {}
+ end
+
+ def [](value)
+ node_for @parent.element.elements[value]
+ end
+
+ def []=(value, key)
+ @parent.element.elements[value.to_s] = key.to_xml_element
+ end
+
+ def each(&block)
+ @parent.element.each_element { |e| yield node_for(e) }
+ end
+
+ private
+
+ def node_for(element)
+ @parent.child_nodes[element] ||= XmlNode.new(element)
+ end
+ end
+
+ # Allows for very pretty xml generation akin to xml builder.
+ #
+ # Example:
+ #
+ # # Create an atom like document
+ # doc = Document.new
+ # doc.root = XmlNode.new 'feed' do |feed|
+ #
+ # feed << XmlNode.new('id', 'tag:atom.com,2007:1')
+ # feed << XmlNode.new('title', 'Atom test feed')
+ # feed << XmlNode.new('author') do |author|
+ # author << XmlNode.new("name", "tobi")
+ # author << XmlNode.new("email", "tobi@gmail.com")
+ # end
+ #
+ # feed << XmlNode.new('entry') do |entry|
+ # entry << XmlNode.new('title', 'First post')
+ # entry << XmlNode.new('summary', 'Lorem ipsum', :type => 'xhtml')
+ # entry << XmlNode.new('created_at', Time.now)
+ # end
+ #
+ # feed << XmlNode.new('dc:published', Time.now)
+ # end
+ #
+ def initialize(node, *args)
+ @element = if node.is_a?(REXML::Element)
+ node
+ else
+ REXML::Element.new(node)
+ end
+
+ @child_nodes = {}
+
+ if attributes = args.last.is_a?(Hash) ? args.pop : nil
+ attributes.each { |k,v| @element.add_attribute(k.to_s, v.to_xml_value) }
+ end
+
+ if !args[0].nil?
+ @element.text = args[0].to_xml_value
+ end
+
+ if block_given?
+ yield self
+ end
+ end
+
+ def self.parse(xml)
+ self.new(REXML::Document.new(xml).root)
+ end
+
+ def children
+ XmlNode::List.new(self)
+ end
+
+ def []=(key, value)
+ @element.attributes[key.to_s] = value.to_xml_value
+ end
+
+ def [](key)
+ @element.attributes[key]
+ end
+
+ # Add a namespace to the node
+ # Example
+ #
+ # node.namespace 'http://www.w3.org/2005/Atom'
+ # node.namespace :opensearch, 'http://a9.com/-/spec/opensearch/1.1/'
+ #
+ def namespace(*args)
+ args[0] = args[0].to_s if args[0].is_a?(Symbol)
+ @element.add_namespace(*args)
+ end
+
+ def cdata=(value)
+ new_cdata = REXML::CData.new( value )
+ @element.children.each do |c|
+ if c.is_a?(REXML::CData)
+ return @element.replace_child(c,new_cdata)
+ end
+ end
+ @element << new_cdata
+ end
+
+ def cdata
+ @element.cdatas.first.to_s
+ end
+
+ def name
+ @element.name
+ end
+
+ def text=(value)
+ @element.text = REXML::Text.new( value )
+ end
+
+ def text
+ @element.text
+ end
+
+ def find(scope, xpath)
+ case scope
+ when :first
+ elem = @element.elements[xpath]
+ elem.nil? ? nil : child_nodes[elem] ||= XmlNode.new(elem)
+ when :all
+ @element.elements.to_a(xpath).collect { |e| child_nodes[e] ||= XmlNode.new(e) }
+ end
+ end
+
+ def <<(elem)
+ case elem
+ when nil then return
+ when Array
+ elem.each { |e| @element << e.to_xml_element }
+ else
+ @element << elem.to_xml_element
+ end
+ end
+
+ def to_xml_element
+ @element
+ end
+
+ def to_s
+ @element.to_s
+ end
+
+ # Use to get pretty formatted xml including DECL
+ # instructions
+ def to_xml
+ xml = []
+ document = REXML::Document.new
+ document << REXML::XMLDecl.new('1.0')
+ document << @element
+ document.write( xml, 0)
+ xml.join
+ end
+
+end
94 test/test_generating.rb
@@ -0,0 +1,94 @@
+require 'rubygems'
+require 'active_support'
+require "test/unit"
+
+require File.dirname(__FILE__) + "/../lib/xml_node"
+
+class TestXmlNode < Test::Unit::TestCase
+
+ def test_init_sanity
+ assert_raise(ArgumentError) { XmlNode.new }
+ assert_nothing_raised { XmlNode.new('feed')}
+ assert_nothing_raised { XmlNode.new('feed', 'content') }
+ assert_nothing_raised { XmlNode.new('feed', :attribute => true) }
+ assert_nothing_raised { XmlNode.new('feed', 'content', :attribute => true) }
+ end
+
+ def test_element_generation
+ assert_equal '<feed/>', XmlNode.new('feed').to_s
+ assert_equal '<feed>content</feed>', XmlNode.new('feed', 'content').to_s
+ assert_equal "<feed attr='true'>content</feed>", XmlNode.new('feed', 'content', :attr => true).to_s
+ assert_equal "<feed attr='true'/>", XmlNode.new('feed', :attr => true).to_s
+ end
+
+ def test_nesting
+ assert_equal '<feed><element/></feed>', XmlNode.new('feed') { |n| n << XmlNode.new('element') }.to_s
+ assert_equal '<feed><element><id>1</id></element></feed>', XmlNode.new('feed') { |n| n << XmlNode.new('element') { |n| n << XmlNode.new('id', '1')} }.to_s
+ end
+
+ def test_cdata
+ node = XmlNode.new('feed')
+ node.text = '...'
+ node.cdata = 'Goodbye world'
+ node.cdata = 'Hello world'
+
+ assert_equal '<feed>...<![CDATA[Hello world]]></feed>', node.to_s
+ assert_equal 'Hello world', node.cdata
+ assert_equal '...', node.text
+ end
+
+ def test_text
+ node = XmlNode.new('feed')
+ node.text = 'Hello world'
+
+ assert_equal '<feed>Hello world</feed>', node.to_s
+ assert_equal 'Hello world', node.text
+ end
+
+ def test_attributes
+ node = XmlNode.new('feed')
+ node['attr'] = 1
+ assert_equal '1', node['attr']
+ end
+
+ def test_namespace
+ node = XmlNode.new('feed')
+ node.namespace 'http://www.w3.org/2005/Atom'
+ assert_equal "<feed xmlns='http://www.w3.org/2005/Atom'/>", node.to_s
+ end
+
+ def test_named_namespace
+ node = XmlNode.new('feed')
+ node.namespace :opensearch, 'http://a9.com/-/spec/opensearch/1.1/'
+ assert_equal "<feed xmlns:opensearch='http://a9.com/-/spec/opensearch/1.1/'/>", node.to_s
+ end
+
+ def test_generate_nice_xml
+ assert_equal "<?xml version='1.0'?>\n<feed>\n <element>test</element>\n <element/>\n</feed>", XmlNode.new('feed') { |n| n << XmlNode.new('element', 'test'); n << XmlNode.new('element') }.to_xml
+ end
+
+ def test_add_array_of_nodes
+ assert_equal '<feed><e>1</e><e>2</e><e>3</e></feed>', XmlNode.new('feed') { |n| n << [1,2,3].collect{ |i| XmlNode.new('e', i) }}.to_s
+ end
+
+ def test_boolean
+ assert_equal '<boolean>true</boolean>', XmlNode.new('boolean', true).to_s
+ assert_equal '<boolean>false</boolean>', XmlNode.new('boolean', false).to_s
+ end
+
+ def test_nil
+ assert_equal '<nil/>', XmlNode.new('nil', nil).to_s
+ end
+
+
+ def test_dont_choke_on_nil_pushing
+ feed = XmlNode.new 'feed'
+ assert_nothing_raised do
+ feed << nil
+ end
+ assert_equal '<feed/>', feed.to_s
+ end
+
+
+
+end
43 test/test_parsing.rb
@@ -0,0 +1,43 @@
+require 'rubygems'
+require 'active_support'
+require "test/unit"
+
+require File.dirname(__FILE__) + "/../lib/xml_node"
+
+class TestXmlNode < Test::Unit::TestCase
+
+ def test_parse_sanity
+ assert_raise(ArgumentError) { XmlNode.parse }
+ assert_nothing_raised { XmlNode.parse('<feed/>') }
+ end
+
+
+ def test_parse_attributes
+ node = XmlNode.parse('<feed attr="1"/>')
+ assert_equal '1', node['attr']
+ assert_equal nil, node['attr2']
+ end
+
+ def test_parse_children
+ node = XmlNode.parse('<feed><element>text</element></feed>')
+ assert_equal XmlNode, node.children['element'].class
+ assert_equal 'text', node.children['element'].text
+ end
+
+ def test_enumerate_children
+ count = 0
+ XmlNode.parse('<feed><element>text</element><element>text</element></feed>').children.each { count += 1 }
+ assert_equal 2, count
+ end
+
+ def test_find_first
+ xml = XmlNode.parse('<feed><elem>1</elem><elem>2</elem><elem>3</elem></feed>')
+ assert_equal '1', xml.find(:first, '//elem').text
+ end
+
+ def test_find_all
+ xml = XmlNode.parse('<feed><elem>1</elem><elem>2</elem><elem>3</elem></feed>')
+ assert_equal ['1', '2', '3'], xml.find(:all, '//elem').collect(&:text)
+ end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.