aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activesupport/CHANGELOG3
-rw-r--r--activesupport/lib/active_support/xml_mini.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb67
-rw-r--r--activesupport/test/xml_mini/nokogiri_engine_test.rb112
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb15
5 files changed, 200 insertions, 1 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 1d42000867..8c6e724606 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,6 +1,7 @@
*Edge*
-* XmlMini supports libxml if available. #2084 [Bart ten Brinke]
+* XmlMini supports LibXML and Nokogiri backends. #2084, #2190 [Bart ten Brinke, Aaron Patterson]
+ Example: XmlMini.backend = 'Nokogiri'
*2.3.1 [RC2] (March 5, 2009)*
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 99158e4ff7..0513c0d4d0 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -8,6 +8,10 @@ module ActiveSupport
extend self
delegate :parse, :to => :@backend
+ class << self
+ attr_reader :backend
+ end
+
def backend=(name)
require "active_support/xml_mini/#{name.to_s.downcase}.rb"
@backend = ActiveSupport.const_get("XmlMini_#{name}")
diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb
new file mode 100644
index 0000000000..bfafa29dd5
--- /dev/null
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -0,0 +1,67 @@
+# = XML Mini Nokogiri implementation
+module ActiveSupport
+ module XmlMini_Nokogiri #:nodoc:
+ extend self
+
+ # Parse an XML Document string into a simple hash using libxml / nokogiri.
+ # string::
+ # XML Document string to parse
+ def parse(string)
+ return {} if string.blank?
+ doc = Nokogiri::XML(string).to_hash
+ end
+
+ module Conversions
+ module Document
+ def to_hash
+ root.to_hash
+ end
+ end
+
+ module Node
+ CONTENT_ROOT = '__content__'
+
+ # Convert XML document to hash
+ #
+ # hash::
+ # Hash to merge the converted element into.
+ def to_hash(hash = {})
+ hash[name] ||= attributes_as_hash
+
+ walker = lambda { |child, memo, callback|
+ next if child.blank?
+
+ if child.text?
+ (memo[CONTENT_ROOT] ||= '') << child.content
+ next
+ end
+
+ name = child.name
+
+ if memo[name]
+ memo[name] = [memo[name]].flatten
+ memo[name] << child.attributes_as_hash
+ else
+ memo[name] = child.attributes_as_hash
+ end
+
+ # Recusively walk children
+ child.children.each { |c| callback.call(c, memo[name], callback) }
+ }
+
+ children.each { |c| walker.call(c, hash[name], walker) }
+ hash
+ end
+
+ def attributes_as_hash
+ Hash[*(attribute_nodes.map { |node|
+ [node.node_name, node.value]
+ }.flatten)]
+ end
+ end
+ end
+
+ Nokogiri::XML::Document.send(:include, Conversions::Document)
+ Nokogiri::XML::Node.send(:include, Conversions::Node)
+ end
+end
diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb
new file mode 100644
index 0000000000..5c4002d34e
--- /dev/null
+++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb
@@ -0,0 +1,112 @@
+require 'abstract_unit'
+require 'active_support/xml_mini'
+
+
+begin
+
+require 'nokogiri'
+
+class NokogiriEngineTest < Test::Unit::TestCase
+ include ActiveSupport
+
+ def setup
+ @default_backend = XmlMini.backend.to_s.split('_').last
+ XmlMini.backend = 'Nokogiri'
+ end
+
+ def teardown
+ XmlMini.backend = @default_backend
+ end
+
+ def test_setting_nokogiri_as_backend
+ XmlMini.backend = 'Nokogiri'
+ assert_equal XmlMini_Nokogiri, XmlMini.backend
+ end
+
+ def test_blank_returns_empty_hash
+ assert_equal({}, XmlMini.parse(nil))
+ assert_equal({}, XmlMini.parse(''))
+ end
+
+ def test_one_node_document_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products/>
+ eoxml
+ end
+
+ def test_one_node_with_attributes_document_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products foo="bar"/>
+ eoxml
+ end
+
+ def test_products_node_with_book_node_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ <book name="awesome" id="12345" />
+ </products>
+ eoxml
+ end
+
+ def test_products_node_with_two_book_nodes_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ <book name="awesome" id="12345" />
+ <book name="america" id="67890" />
+ </products>
+ eoxml
+ end
+
+ def test_single_node_with_content_as_hash
+ assert_equal_rexml(<<-eoxml)
+ <products>
+ hello world
+ </products>
+ eoxml
+ end
+
+ def test_children_with_children
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ <book name="america" id="67890" />
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_text
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ <products>
+ hello everyone
+ </products>
+ </root>
+ eoxml
+ end
+
+ def test_children_with_non_adjacent_text
+ assert_equal_rexml(<<-eoxml)
+ <root>
+ good
+ <products>
+ hello everyone
+ </products>
+ morning
+ </root>
+ eoxml
+ end
+
+ private
+ def assert_equal_rexml(xml)
+ XmlMini.backend = 'REXML'
+ hash = XmlMini.parse(xml)
+
+ XmlMini.backend = 'Nokogiri'
+
+ assert_equal(hash, XmlMini.parse(xml))
+ end
+end
+rescue LoadError
+ # Yay, no errors
+end
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
new file mode 100644
index 0000000000..a412d8ca05
--- /dev/null
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -0,0 +1,15 @@
+require 'abstract_unit'
+require 'active_support/xml_mini'
+
+class REXMLEngineTest < Test::Unit::TestCase
+ include ActiveSupport
+
+ def test_default_is_rexml
+ assert_equal XmlMini_REXML, XmlMini.backend
+ end
+
+ def test_set_rexml_as_backend
+ XmlMini.backend = 'REXML'
+ assert_equal XmlMini_REXML, XmlMini.backend
+ end
+end