aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2009-03-12 15:31:24 +0000
committerPratik Naik <pratiknaik@gmail.com>2009-03-12 15:31:24 +0000
commit53744c543880999a7ad3f1e026875df3283978f1 (patch)
tree8d01e0c15a384a46db176ad8d5cdd0bd6c9024a8 /activesupport
parent053afbe3bd26d627ee04a0aaa73554b7be422f05 (diff)
parent47bdf3bf40ec17e1f8ca1c0e3d7f697d0c4cd1bf (diff)
downloadrails-53744c543880999a7ad3f1e026875df3283978f1.tar.gz
rails-53744c543880999a7ad3f1e026875df3283978f1.tar.bz2
rails-53744c543880999a7ad3f1e026875df3283978f1.zip
Merge commit 'mainstream/master'
Conflicts: actionpack/lib/action_view/helpers/text_helper.rb activesupport/lib/active_support/inflector.rb
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG3
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb6
-rw-r--r--activesupport/lib/active_support/inflector.rb18
-rw-r--r--activesupport/lib/active_support/json/decoding.rb22
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb7
-rw-r--r--activesupport/lib/active_support/xml_mini.rb19
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb16
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogiri.rb77
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb14
-rw-r--r--activesupport/test/inflector_test.rb6
-rw-r--r--activesupport/test/json/decoding_test.rb6
-rw-r--r--activesupport/test/xml_mini/nokogiri_engine_test.rb157
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb15
13 files changed, 342 insertions, 24 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/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index c96c5160b3..34ba8a005d 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -91,6 +91,12 @@ class HashWithIndifferentAccess < Hash
self.dup.update(hash)
end
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
+ def reverse_merge(other_hash)
+ super other_hash.with_indifferent_access
+ end
+
# Removes a specified key from the hash.
def delete(key)
super(convert_key(key))
diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb
index 2e2a63c3e7..3ed30bdf56 100644
--- a/activesupport/lib/active_support/inflector.rb
+++ b/activesupport/lib/active_support/inflector.rb
@@ -257,20 +257,22 @@ module ActiveSupport
# <%= link_to(@person.name, person_path(@person)) %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(string, sep = '-')
- re_sep = Regexp.escape(sep)
- # Replace accented chars with their ASCII equivalents.
+ # replace accented chars with ther ascii equivalents
parameterized_string = transliterate(string)
- # Turn unwanted chars into the separator.
+ # Turn unwanted chars into the seperator
parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep)
- # No more than one of the separator in a row.
- parameterized_string.squeeze!(sep)
- # Remove leading/trailing separator.
- parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
+ unless sep.blank?
+ re_sep = Regexp.escape(sep)
+ # No more than one of the separator in a row.
+ parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
+ # Remove leading/trailing separator.
+ parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
+ end
parameterized_string.downcase
end
- # Replaces accented characters with their ASCII equivalents.
+ # Replaces accented characters with their ascii equivalents.
def transliterate(string)
Iconv.iconv('ascii//ignore//translit', 'utf-8', string).to_s
end
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index 5eb8c0fd7d..0e079341ff 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -43,14 +43,32 @@ module ActiveSupport
end
if marks.empty?
- json.gsub(/\\\//, '/')
+ json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
+ ustr = $1
+ if ustr.starts_with?('u')
+ [ustr[1..-1].to_i(16)].pack("U")
+ elsif ustr == '\\'
+ '\\\\'
+ else
+ ustr
+ end
+ end
else
left_pos = [-1].push(*marks)
right_pos = marks << scanner.pos + scanner.rest_size
output = []
left_pos.each_with_index do |left, i|
scanner.pos = left.succ
- output << scanner.peek(right_pos[i] - scanner.pos + 1)
+ output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
+ ustr = $1
+ if ustr.starts_with?('u')
+ [ustr[1..-1].to_i(16)].pack("U")
+ elsif ustr == '\\'
+ '\\\\'
+ else
+ ustr
+ end
+ end
end
output = output * " "
diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb
index 7edf6fdb32..aaf9f8f42c 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -45,7 +45,12 @@ module ActiveSupport
return if @method_name.to_s == "default_test"
if using_mocha = respond_to?(:mocha_verify)
- assertion_counter = Mocha::TestCaseAdapter::AssertionCounter.new(result)
+ assertion_counter_klass = if defined?(Mocha::TestCaseAdapter::AssertionCounter)
+ Mocha::TestCaseAdapter::AssertionCounter
+ else
+ Mocha::Integration::TestUnit::AssertionCounter
+ end
+ assertion_counter = assertion_counter_klass.new(result)
end
yield(Test::Unit::TestCase::STARTED, name)
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 99158e4ff7..ccd1349491 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -6,11 +6,24 @@ module ActiveSupport
# XmlMini.backend = 'LibXML'
module XmlMini
extend self
- delegate :parse, :to => :@backend
+
+ attr_reader :backend
+ delegate :parse, :to => :backend
def backend=(name)
- require "active_support/xml_mini/#{name.to_s.downcase}.rb"
- @backend = ActiveSupport.const_get("XmlMini_#{name}")
+ if name.is_a?(Module)
+ @backend = name
+ else
+ require "active_support/xml_mini/#{name.to_s.downcase}.rb"
+ @backend = ActiveSupport.const_get("XmlMini_#{name}")
+ end
+ end
+
+ def with_backend(name)
+ old_backend, self.backend = backend, name
+ yield
+ ensure
+ self.backend = old_backend
end
end
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index e1549d8c58..3586b24a6b 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -1,4 +1,6 @@
-# = XML Mini Libxml implementation
+require 'libxml'
+
+# = XmlMini LibXML implementation
module ActiveSupport
module XmlMini_LibXML #:nodoc:
extend self
@@ -7,19 +9,19 @@ module ActiveSupport
# string::
# XML Document string to parse
def parse(string)
- XML.default_keep_blanks = false
+ LibXML::XML.default_keep_blanks = false
if string.blank?
{}
else
- XML::Parser.string(string.strip).parse.to_hash
+ LibXML::XML::Parser.string(string.strip).parse.to_hash
end
end
end
end
-module XML
+module LibXML
module Conversions
module Document
def to_hash
@@ -37,7 +39,7 @@ module XML
# Hash to merge the converted element into.
def to_hash(hash={})
if text?
- raise RuntimeError if content.length >= LIB_XML_LIMIT
+ raise LibXML::XML::Error if content.length >= LIB_XML_LIMIT
hash[CONTENT_ROOT] = content
else
sub_hash = insert_name_into_hash(hash, name)
@@ -127,5 +129,5 @@ module XML
end
end
-XML::Document.send(:include, XML::Conversions::Document)
-XML::Node.send(:include, XML::Conversions::Node)
+LibXML::XML::Document.send(:include, LibXML::Conversions::Document)
+LibXML::XML::Node.send(:include, LibXML::Conversions::Node)
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..10281584fc
--- /dev/null
+++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb
@@ -0,0 +1,77 @@
+require 'nokogiri'
+
+# = XmlMini 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)
+ if string.blank?
+ {}
+ else
+ doc = Nokogiri::XML(string)
+ raise doc.errors.first if doc.errors.length > 0
+ doc.to_hash
+ end
+ 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 { |memo, parent, child, callback|
+ next if child.blank? && 'file' != parent['type']
+
+ if child.text?
+ (memo[CONTENT_ROOT] ||= '') << child.content
+ next
+ end
+
+ name = child.name
+
+ child_hash = child.attributes_as_hash
+ if memo[name]
+ memo[name] = [memo[name]].flatten
+ memo[name] << child_hash
+ else
+ memo[name] = child_hash
+ end
+
+ # Recusively walk children
+ child.children.each { |c|
+ callback.call(child_hash, child, c, callback)
+ }
+ }
+
+ children.each { |c| walker.call(hash[name], self, c, 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/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 2285d5a724..482ae57830 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -174,6 +174,13 @@ class HashExtTest < Test::Unit::TestCase
assert_equal 2, hash['b']
end
+ def test_indifferent_reverse_merging
+ hash = HashWithIndifferentAccess.new('some' => 'value', 'other' => 'value')
+ hash.reverse_merge!(:some => 'noclobber', :another => 'clobber')
+ assert_equal 'value', hash[:some]
+ assert_equal 'clobber', hash[:another]
+ end
+
def test_indifferent_deleting
get_hash = proc{ { :a => 'foo' }.with_indifferent_access }
hash = get_hash.call
@@ -884,7 +891,12 @@ class QueryTest < Test::Unit::TestCase
end
def test_expansion_count_is_limited
- expected = defined?(LibXML) ? LibXML::XML::Error : RuntimeError
+ expected = {
+ 'ActiveSupport::XmlMini_REXML' => 'RuntimeError',
+ 'ActiveSupport::XmlMini_Nokogiri' => 'Nokogiri::XML::SyntaxError',
+ 'ActiveSupport::XmlMini_LibXML' => 'LibXML::XML::Error',
+ }[ActiveSupport::XmlMini.backend.name].constantize
+
assert_raise expected do
attack_xml = <<-EOT
<?xml version="1.0" encoding="UTF-8"?>
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index 948b6d9bb0..6b9fbd3156 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -116,6 +116,12 @@ class InflectorTest < Test::Unit::TestCase
end
end
+ def test_parameterize_with_multi_character_separator
+ StringToParameterized.each do |some_string, parameterized_string|
+ assert_equal(parameterized_string.gsub('-', '__sep__'), ActiveSupport::Inflector.parameterize(some_string, '__sep__'))
+ end
+ end
+
def test_classify
ClassNameToTableName.each do |class_name, table_name|
assert_equal(class_name, ActiveSupport::Inflector.classify(table_name))
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index b88a00e584..8fe40557d6 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -28,7 +28,11 @@ class TestJSONDecoding < Test::Unit::TestCase
%(null) => nil,
%(true) => true,
%(false) => false,
- %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1"
+ %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1",
+ %q("\u003cunicode\u0020escape\u003e") => "<unicode escape>",
+ %q("\\\\u0020skip double backslashes") => "\\u0020skip double backslashes",
+ %q({a: "\u003cbr /\u003e"}) => {'a' => "<br />"},
+ %q({b:["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]}
}
TESTS.each do |json, expected|
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..e5174a0b57
--- /dev/null
+++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb
@@ -0,0 +1,157 @@
+require 'abstract_unit'
+require 'active_support/xml_mini'
+
+begin
+ gem 'nokogiri', '>= 1.1.1'
+rescue Gem::LoadError
+ # Skip nokogiri tests
+else
+
+require 'nokogiri'
+
+class NokogiriEngineTest < Test::Unit::TestCase
+ include ActiveSupport
+
+ def setup
+ @default_backend = XmlMini.backend
+ XmlMini.backend = 'Nokogiri'
+ end
+
+ def teardown
+ XmlMini.backend = @default_backend
+ end
+
+ def test_file_from_xml
+ hash = Hash.from_xml(<<-eoxml)
+ <blog>
+ <logo type="file" name="logo.png" content_type="image/png">
+ </logo>
+ </blog>
+ 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 Nokogiri::XML::SyntaxError do
+ attack_xml = <<-EOT
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE member [
+ <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
+ <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
+ <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
+ <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
+ <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
+ <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
+ <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
+ ]>
+ <member>
+ &a;
+ </member>
+ EOT
+ Hash.from_xml(attack_xml)
+ end
+ 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_array_type_makes_an_array
+ assert_equal_rexml(<<-eoxml)
+ <blog>
+ <posts type="array">
+ <post>a post</post>
+ <post>another post</post>
+ </posts>
+ </blog>
+ eoxml
+ 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)
+ hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
+ assert_equal(hash, XmlMini.parse(xml))
+ end
+end
+
+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