From f7ee8779fd1b00ab7ce06cd148cb7a31ed528de5 Mon Sep 17 00:00:00 2001 From: Timothy Elliott Date: Fri, 1 May 2009 14:50:05 -0700 Subject: [PATCH] Add Hpricot backend for XmlMini Based on the Nokogiri implementation. Two test cases from the Nokogiri implementation are omitted because Hpricot does not support entities or file references. --- .../lib/active_support/xml_mini/hpricot.rb | 64 ++++++++ activesupport/test/xml_mini/hpricot_engine_test.rb | 158 ++++++++++++++++++++ 2 files changed, 222 insertions(+), 0 deletions(-) create mode 100644 activesupport/lib/active_support/xml_mini/hpricot.rb create mode 100644 activesupport/test/xml_mini/hpricot_engine_test.rb diff --git a/activesupport/lib/active_support/xml_mini/hpricot.rb b/activesupport/lib/active_support/xml_mini/hpricot.rb new file mode 100644 index 0000000..22e9508 --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/hpricot.rb @@ -0,0 +1,64 @@ +require 'hpricot' + +# = XmlMini Hpricot implementation. Based on the Nokogiri implementation. +module ActiveSupport + module XmlMini_Hpricot #:nodoc: + extend self + + # Parse an XML Document string into a simple hash using Hpricot. + # string:: + # XML Document string to parse + def parse(string) + if string.blank? + {} + else + doc = Hpricot.XML(string) + doc.to_hash + end + end + + module Conversions #:nodoc: + module Document #:nodoc: + def to_hash + root.to_hash + end + end + + module Node #:nodoc: class Hpricot::Elem + CONTENT_ROOT = '__content__' + + def to_hash(hash = {}) + hash[name] ||= attributes + walker = lambda { |memo, child, callback| + + if [Hpricot::Text, Hpricot::CData].include? child.class + unless child.inner_text.strip.empty? + (memo[CONTENT_ROOT] ||= '') << child.inner_text + end + next + end + + name = child.name + + child_hash = child.attributes + if memo[name] + memo[name] = [memo[name]].flatten + memo[name] << child_hash + else + memo[name] = child_hash + end + + # Recusively walk children + child.each_child{ |c| callback.call(child_hash, c, callback) } + } + + each_child{ |c| walker.call(hash[name], c, walker) } + hash + end + end + end + + Hpricot::Doc.send(:include, Conversions::Document) + Hpricot::Elem.send(:include, Conversions::Node) + end +end diff --git a/activesupport/test/xml_mini/hpricot_engine_test.rb b/activesupport/test/xml_mini/hpricot_engine_test.rb new file mode 100644 index 0000000..e0b6cdb --- /dev/null +++ b/activesupport/test/xml_mini/hpricot_engine_test.rb @@ -0,0 +1,158 @@ +require 'abstract_unit' +require 'active_support/xml_mini' +require 'active_support/core_ext/hash/conversions' + +begin + gem 'hpricot' +rescue Gem::LoadError + # Skip hpricot tests +else + +require 'hpricot' + +class HpricotEngineTest < Test::Unit::TestCase + include ActiveSupport + + def setup + @default_backend = XmlMini.backend + XmlMini.backend = 'Hpricot' + end + + def teardown + XmlMini.backend = @default_backend + end + +# def test_file_from_xml +# hash = Hash.from_xml(<<-eoxml) +# +# +# +# +# eoxml +# assert hash.has_key?('blog') +# assert hash['blog'].has_key?('logo') +# +# file = hash['blog']['logo'] +# assert_equal 'logo.png', file.original_filename +# assert_equal 'image/png', file.content_type +# end + +# def test_exception_thrown_on_expansion_attack +# assert_raise Hpricot::XML::SyntaxError do +# attack_xml = <<-EOT +# +# +# +# +# +# +# +# +# ]> +# +# &a; +# +# EOT +# Hash.from_xml(attack_xml) +# end +# end + + def test_setting_hpricot_as_backend + XmlMini.backend = 'Hpricot' + assert_equal XmlMini_Hpricot, XmlMini.backend + end + + def test_blank_returns_empty_hash + assert_equal({}, XmlMini.parse(nil)) + assert_equal({}, XmlMini.parse('')) + end + + def test_array_type_makes_an_array + assert_equal_rexml(<<-eoxml) + + + a post + another post + + + eoxml + end + + def test_one_node_document_as_hash + assert_equal_rexml(<<-eoxml) + + eoxml + end + + def test_one_node_with_attributes_document_as_hash + assert_equal_rexml(<<-eoxml) + + eoxml + end + + def test_products_node_with_book_node_as_hash + assert_equal_rexml(<<-eoxml) + + + + eoxml + end + + def test_products_node_with_two_book_nodes_as_hash + assert_equal_rexml(<<-eoxml) + + + + + eoxml + end + + def test_single_node_with_content_as_hash + assert_equal_rexml(<<-eoxml) + + hello world + + eoxml + end + + def test_children_with_children + assert_equal_rexml(<<-eoxml) + + + + + + eoxml + end + + def test_children_with_text + assert_equal_rexml(<<-eoxml) + + + hello everyone + + + eoxml + end + + def test_children_with_non_adjacent_text + assert_equal_rexml(<<-eoxml) + + good + + hello everyone + + morning + + eoxml + end + + private + def assert_equal_rexml(xml) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, XmlMini.parse(xml)) + end +end + +end -- 1.5.4.3