diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2009-03-10 00:33:32 +0000 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2009-03-10 00:33:32 +0000 |
commit | 1ab84247ca4b80afbd82afe7539b3449636344c4 (patch) | |
tree | 9ca5e3bee3a07b68c9d900ff129b69bc740405c2 /activesupport/lib/active_support | |
parent | b6fb79c531a0079274f7b459302e6290c9183f28 (diff) | |
parent | 4458edc882b229ea44602da20a6440a6f233f1c8 (diff) | |
download | rails-1ab84247ca4b80afbd82afe7539b3449636344c4.tar.gz rails-1ab84247ca4b80afbd82afe7539b3449636344c4.tar.bz2 rails-1ab84247ca4b80afbd82afe7539b3449636344c4.zip |
Merge commit 'mainstream/master'
Conflicts:
railties/guides/files/stylesheets/main.css
railties/guides/rails_guides/generator.rb
railties/guides/source/index.erb.textile
Diffstat (limited to 'activesupport/lib/active_support')
26 files changed, 341 insertions, 149 deletions
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index a9b50ca92d..ba8e022fb2 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -7,10 +7,9 @@ module ActiveSupport #:nodoc: # * <tt>:two_words_connector</tt> - The sign or word used to join the elements in arrays with two elements (default: " and ") # * <tt>:last_word_connector</tt> - The sign or word used to join the last element in arrays with three or more elements (default: ", and ") def to_sentence(options = {}) - - default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) + default_words_connector = I18n.translate(:'support.array.words_connector', :locale => options[:locale]) default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) - default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) + default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) # Try to emulate to_senteces previous to 2.3 if options.has_key?(:connector) || options.has_key?(:skip_last_comma) diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index 15ad3d99cc..48e812aaf8 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -102,8 +102,8 @@ module ActiveSupport #:nodoc: # # <%= link_to(@person.name, person_path %> # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a> - def parameterize - Inflector.parameterize(self) + def parameterize(sep = '-') + Inflector.parameterize(self, sep) end # Creates the name of a table like Rails does for models to table names. This method diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index c41e86dfd1..f64661c5b1 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -70,7 +70,7 @@ module ActiveSupport [:years, :months, :days, :minutes, :seconds].map do |length| n = consolidated[length] "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero? - end.compact.to_sentence + end.compact.to_sentence(:locale => :en) end protected diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 952b4d8063..71cfe61739 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -89,6 +89,10 @@ module ActiveSupport end # end end # end end # end + # + if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type) + private #{symbol.inspect} # private :mime_type + end # end EOS end end diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 62b6d798ef..60f082bcc1 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -344,7 +344,19 @@ module ActiveSupport #:nodoc: end alias_method :[], :slice - # Converts first character in the string to Unicode value + # Like <tt>String#slice!</tt>, except instead of byte offsets you specify character offsets. + # + # Example: + # s = 'こんにちは' + # s.mb_chars.slice!(2..3).to_s #=> "にち" + # s #=> "こんは" + def slice!(*args) + slice = self[*args] + self[*args] = '' + slice + end + + # Returns the codepoint of the first character in the string. # # Example: # 'こんにちは'.mb_chars.ord #=> 12371 @@ -432,7 +444,7 @@ module ActiveSupport #:nodoc: chars(self.class.tidy_bytes(@wrapped_string)) end - %w(lstrip rstrip strip reverse upcase downcase slice tidy_bytes capitalize).each do |method| + %w(lstrip rstrip strip reverse upcase downcase tidy_bytes capitalize).each do |method| define_method("#{method}!") do |*args| unless args.nil? @wrapped_string = send(method, *args).to_s diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 66aab9e562..fed8094a24 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -54,7 +54,7 @@ module ActiveSupport end def to_hash - Hash.new(self) + self end def each_key @@ -93,7 +93,7 @@ module ActiveSupport end def inspect - "#<OrderedHash #{self.to_hash.inspect}>" + "#<OrderedHash #{super}>" end private diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 97b2b6ef9c..f05d4098fc 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -20,7 +20,7 @@ module ActiveSupport alias_method :method_name, :name else # TODO: Figure out how to get the Rails::BacktraceFilter into minitest/unit - if defined?(Rails) + if defined?(Rails) && ENV['BACKTRACE'].nil? require 'rails/backtrace_cleaner' Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit } end diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 39da70a9f3..28852e65c8 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -22,8 +22,8 @@ end # TODO I18n gem has not been released yet # begin -# gem 'i18n', '~> 0.1.1' +# gem 'i18n', '~> 0.1.3' # rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.1/lib" + $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.3/lib" require 'i18n' # end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore b/activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore deleted file mode 100644 index 0f41a39f89..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -test/rails/fixtures -doc diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/MIT-LICENSE b/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE index ed8e9ee66d..ed8e9ee66d 100755 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/MIT-LICENSE +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/README.textile b/activesupport/lib/active_support/vendor/i18n-0.1.3/README.textile index a07fc8426d..a07fc8426d 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/README.textile +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/README.textile diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/Rakefile b/activesupport/lib/active_support/vendor/i18n-0.1.3/Rakefile index 2164e13e69..2164e13e69 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/Rakefile +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/Rakefile diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/i18n.gemspec b/activesupport/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec index 14294606bd..f102689a6f 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/i18n.gemspec +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec @@ -1,7 +1,7 @@ Gem::Specification.new do |s| s.name = "i18n" - s.version = "0.1.1" - s.date = "2008-10-26" + s.version = "0.1.3" + s.date = "2009-01-09" s.summary = "Internationalization support for Ruby" s.email = "rails-i18n@googlegroups.com" s.homepage = "http://rails-i18n.org" diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb index 76361bed90..76361bed90 100755 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb index b54164d496..c09acd7d2d 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/backend/simple.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb @@ -151,12 +151,7 @@ module I18n def interpolate(locale, string, values = {}) return string unless string.is_a?(String) - if string.respond_to?(:force_encoding) - original_encoding = string.encoding - string.force_encoding(Encoding::BINARY) - end - - result = string.gsub(MATCH) do + string.gsub(MATCH) do escaped, pattern, key = $1, $2, $2.to_sym if escaped @@ -169,9 +164,6 @@ module I18n values[key].to_s end end - - result.force_encoding(original_encoding) if original_encoding - result end # Loads a single translations file by delegating to #load_rb or diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb index b5cea7acb4..b5cea7acb4 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/exceptions.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/all.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/all.rb index 353712da49..353712da49 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/all.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/all.rb diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_exceptions_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb index dfcba6901f..dfcba6901f 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_exceptions_test.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb index bbb35ec809..50d6832c9e 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_test.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb @@ -116,10 +116,10 @@ class I18nTest < Test::Unit::TestCase end def test_localize_nil_raises_argument_error - assert_raises(I18n::ArgumentError) { I18n.l nil } + assert_raise(I18n::ArgumentError) { I18n.l nil } end def test_localize_object_raises_argument_error - assert_raises(I18n::ArgumentError) { I18n.l Object.new } + assert_raise(I18n::ArgumentError) { I18n.l Object.new } end end diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb index 6044ce10d9..6044ce10d9 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.yml b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml index 0b298c9c0e..0b298c9c0e 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.yml +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/simple_backend_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb index 8ba7036abf..65f3ac11a6 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/simple_backend_test.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb @@ -155,7 +155,7 @@ class I18nSimpleBackendTranslateTest < Test::Unit::TestCase end def test_translate_given_an_array_of_inexistent_keys_it_raises_missing_translation_data - assert_raises I18n::MissingTranslationData do + assert_raise I18n::MissingTranslationData do @backend.translate('en', :does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :does_not_exist_3]) end end @@ -180,11 +180,11 @@ class I18nSimpleBackendTranslateTest < Test::Unit::TestCase end def test_translate_given_nil_as_a_locale_raises_an_argument_error - assert_raises(I18n::InvalidLocale){ @backend.translate nil, :bar } + assert_raise(I18n::InvalidLocale){ @backend.translate nil, :bar } end def test_translate_with_a_bogus_key_and_no_default_raises_missing_translation_data - assert_raises(I18n::MissingTranslationData){ @backend.translate 'de', :bogus } + assert_raise(I18n::MissingTranslationData){ @backend.translate 'de', :bogus } end end @@ -230,15 +230,15 @@ class I18nSimpleBackendPluralizeTest < Test::Unit::TestCase end def test_interpolate_given_incomplete_pluralization_data_raises_invalid_pluralization_data - assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, {:one => 'bar'}, 2) } + assert_raise(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, {:one => 'bar'}, 2) } end # def test_interpolate_given_a_string_raises_invalid_pluralization_data - # assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, 'bar', 2) } + # assert_raise(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, 'bar', 2) } # end # # def test_interpolate_given_an_array_raises_invalid_pluralization_data - # assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, ['bar'], 2) } + # assert_raise(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, ['bar'], 2) } # end end @@ -253,6 +253,32 @@ class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase assert_equal 'Häi David!', @backend.send(:interpolate, nil, 'Häi {{name}}!', :name => 'David') end + def test_interpolate_given_an_unicode_value_hash_interpolates_to_the_string + assert_equal 'Hi ゆきひろ!', @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => 'ゆきひろ') + end + + def test_interpolate_given_an_unicode_value_hash_interpolates_into_unicode_string + assert_equal 'こんにちは、ゆきひろさん!', @backend.send(:interpolate, nil, 'こんにちは、{{name}}さん!', :name => 'ゆきひろ') + end + + if Kernel.const_defined?(:Encoding) + def test_interpolate_given_a_non_unicode_multibyte_value_hash_interpolates_into_a_string_with_the_same_encoding + assert_equal euc_jp('Hi ゆきひろ!'), @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => euc_jp('ゆきひろ')) + end + + def test_interpolate_given_an_unicode_value_hash_into_a_non_unicode_multibyte_string_raises_encoding_compatibility_error + assert_raise(Encoding::CompatibilityError) do + @backend.send(:interpolate, nil, euc_jp('こんにちは、{{name}}さん!'), :name => 'ゆきひろ') + end + end + + def test_interpolate_given_a_non_unicode_multibyte_value_hash_into_an_unicode_string_raises_encoding_compatibility_error + assert_raise(Encoding::CompatibilityError) do + @backend.send(:interpolate, nil, 'こんにちは、{{name}}さん!', :name => euc_jp('ゆきひろ')) + end + end + end + def test_interpolate_given_nil_as_a_string_returns_nil assert_nil @backend.send(:interpolate, nil, nil, :name => 'David') end @@ -266,11 +292,17 @@ class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase end def test_interpolate_given_an_empty_values_hash_raises_missing_interpolation_argument - assert_raises(I18n::MissingInterpolationArgument) { @backend.send(:interpolate, nil, 'Hi {{name}}!', {}) } + assert_raise(I18n::MissingInterpolationArgument) { @backend.send(:interpolate, nil, 'Hi {{name}}!', {}) } end def test_interpolate_given_a_string_containing_a_reserved_key_raises_reserved_interpolation_key - assert_raises(I18n::ReservedInterpolationKey) { @backend.send(:interpolate, nil, '{{default}}', {:default => nil}) } + assert_raise(I18n::ReservedInterpolationKey) { @backend.send(:interpolate, nil, '{{default}}', {:default => nil}) } + end + + private + + def euc_jp(string) + string.encode!(Encoding::EUC_JP) end end @@ -320,11 +352,11 @@ class I18nSimpleBackendLocalizeDateTest < Test::Unit::TestCase end def test_localize_nil_raises_argument_error - assert_raises(I18n::ArgumentError) { @backend.localize 'de', nil } + assert_raise(I18n::ArgumentError) { @backend.localize 'de', nil } end def test_localize_object_raises_argument_error - assert_raises(I18n::ArgumentError) { @backend.localize 'de', Object.new } + assert_raise(I18n::ArgumentError) { @backend.localize 'de', Object.new } end end @@ -454,7 +486,7 @@ class I18nSimpleBackendLoadTranslationsTest < Test::Unit::TestCase include I18nSimpleBackendTestSetup def test_load_translations_with_unknown_file_type_raises_exception - assert_raises(I18n::UnknownFileType) { @backend.load_translations "#{@locale_dir}/en.xml" } + assert_raise(I18n::UnknownFileType) { @backend.load_translations "#{@locale_dir}/en.xml" } end def test_load_translations_with_ruby_file_type_does_not_raise_exception @@ -485,6 +517,10 @@ end class I18nSimpleBackendLoadPathTest < Test::Unit::TestCase include I18nSimpleBackendTestSetup + def teardown + I18n.load_path = [] + end + def test_nested_load_paths_do_not_break_locale_loading @backend = I18n::Backend::Simple.new I18n.load_path = [[File.dirname(__FILE__) + '/locale/en.yml']] @@ -492,6 +528,14 @@ class I18nSimpleBackendLoadPathTest < Test::Unit::TestCase assert_nothing_raised { @backend.send :init_translations } assert_not_nil backend_get_translations end + + def test_adding_arrays_of_filenames_to_load_path_do_not_break_locale_loading + @backend = I18n::Backend::Simple.new + I18n.load_path << Dir[File.dirname(__FILE__) + '/locale/*.{rb,yml}'] + assert_nil backend_get_translations + assert_nothing_raised { @backend.send :init_translations } + assert_not_nil backend_get_translations + end end class I18nSimpleBackendReloadTranslationsTest < Test::Unit::TestCase @@ -521,4 +565,4 @@ class I18nSimpleBackendReloadTranslationsTest < Test::Unit::TestCase @backend.reload! assert_equal @backend.initialized?, false end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 3e2b29b327..8b65fffa08 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index ce3f50620d..99158e4ff7 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -1,113 +1,18 @@ -# = XmlMini -# This is a derivitive work of XmlSimple 1.0.11 -# Author:: Joseph Holsten <joseph@josephholsten.com> -# Copyright:: Copyright (c) 2008 Joseph Holsten -# Copyright:: Copyright (c) 2003-2006 Maik Schmidt <contact@maik-schmidt.de> -# License:: Distributes under the same terms as Ruby. module ActiveSupport + # = XmlMini + # + # To use the much faster libxml parser: + # gem 'libxml-ruby', '=0.9.7' + # XmlMini.backend = 'LibXML' module XmlMini extend self + delegate :parse, :to => :@backend - CONTENT_KEY = '__content__'.freeze - - # Parse an XML Document string into a simple hash - # - # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, - # and uses the defaults from ActiveSupport - # - # string:: - # XML Document string to parse - def parse(string) - require 'rexml/document' unless defined?(REXML::Document) - doc = REXML::Document.new(string) - merge_element!({}, doc.root) + def backend=(name) + require "active_support/xml_mini/#{name.to_s.downcase}.rb" + @backend = ActiveSupport.const_get("XmlMini_#{name}") end - - private - # Convert an XML element and merge into the hash - # - # hash:: - # Hash to merge the converted element into. - # element:: - # XML element to merge into hash - def merge_element!(hash, element) - merge!(hash, element.name, collapse(element)) - end - - # Actually converts an XML document element into a data structure. - # - # element:: - # The document element to be collapsed. - def collapse(element) - hash = get_attributes(element) - - if element.has_elements? - element.each_element {|child| merge_element!(hash, child) } - merge_texts!(hash, element) unless empty_content?(element) - hash - else - merge_texts!(hash, element) - end - end - - # Merge all the texts of an element into the hash - # - # hash:: - # Hash to add the converted emement to. - # element:: - # XML element whose texts are to me merged into the hash - def merge_texts!(hash, element) - unless element.has_text? - hash - else - # must use value to prevent double-escaping - merge!(hash, CONTENT_KEY, element.texts.sum(&:value)) - end - end - - # Adds a new key/value pair to an existing Hash. If the key to be added - # already exists and the existing value associated with key is not - # an Array, it will be wrapped in an Array. Then the new value is - # appended to that Array. - # - # hash:: - # Hash to add key/value pair to. - # key:: - # Key to be added. - # value:: - # Value to be associated with key. - def merge!(hash, key, value) - if hash.has_key?(key) - if hash[key].instance_of?(Array) - hash[key] << value - else - hash[key] = [hash[key], value] - end - elsif value.instance_of?(Array) - hash[key] = [value] - else - hash[key] = value - end - hash - end - - # Converts the attributes array of an XML element into a hash. - # Returns an empty Hash if node has no attributes. - # - # element:: - # XML element to extract attributes from. - def get_attributes(element) - attributes = {} - element.attributes.each { |n,v| attributes[n] = v } - attributes - end - - # Determines if a document element has text content - # - # element:: - # XML element to be checked. - def empty_content?(element) - element.texts.join.blank? - end end -end
\ No newline at end of file + + XmlMini.backend = 'REXML' +end diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb new file mode 100644 index 0000000000..e1549d8c58 --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -0,0 +1,131 @@ +# = XML Mini Libxml implementation +module ActiveSupport + module XmlMini_LibXML #:nodoc: + extend self + + # Parse an XML Document string into a simple hash using libxml. + # string:: + # XML Document string to parse + def parse(string) + XML.default_keep_blanks = false + + if string.blank? + {} + else + XML::Parser.string(string.strip).parse.to_hash + end + end + + end +end + +module XML + module Conversions + module Document + def to_hash + root.to_hash + end + end + + module Node + CONTENT_ROOT = '__content__' + LIB_XML_LIMIT = 30000000 # Hardcoded LibXML limit + + # Convert XML document to hash + # + # hash:: + # Hash to merge the converted element into. + def to_hash(hash={}) + if text? + raise RuntimeError if content.length >= LIB_XML_LIMIT + hash[CONTENT_ROOT] = content + else + sub_hash = insert_name_into_hash(hash, name) + attributes_to_hash(sub_hash) + if array? + children_array_to_hash(sub_hash) + elsif yaml? + children_yaml_to_hash(sub_hash) + else + children_to_hash(sub_hash) + end + end + hash + end + + protected + + # Insert name into hash + # + # hash:: + # Hash to merge the converted element into. + # name:: + # name to to merge into hash + def insert_name_into_hash(hash, name) + sub_hash = {} + if hash[name] + if !hash[name].kind_of? Array + hash[name] = [hash[name]] + end + hash[name] << sub_hash + else + hash[name] = sub_hash + end + sub_hash + end + + # Insert children into hash + # + # hash:: + # Hash to merge the children into. + def children_to_hash(hash={}) + each { |child| child.to_hash(hash) } + attributes_to_hash(hash) + hash + end + + # Convert xml attributes to hash + # + # hash:: + # Hash to merge the attributes into + def attributes_to_hash(hash={}) + each_attr { |attr| hash[attr.name] = attr.value } + hash + end + + # Convert array into hash + # + # hash:: + # Hash to merge the array into + def children_array_to_hash(hash={}) + hash[child.name] = map do |child| + returning({}) { |sub_hash| child.children_to_hash(sub_hash) } + end + hash + end + + # Convert yaml into hash + # + # hash:: + # Hash to merge the yaml into + def children_yaml_to_hash(hash = {}) + hash[CONTENT_ROOT] = content unless content.blank? + hash + end + + # Check if child is of type array + def array? + child? && child.next? && child.name == child.next.name + end + + # Check if child is of type yaml + def yaml? + attributes.collect{|x| x.value}.include?('yaml') + end + + end + end +end + +XML::Document.send(:include, XML::Conversions::Document) +XML::Node.send(:include, XML::Conversions::Node) diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb new file mode 100644 index 0000000000..a8fdeca967 --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -0,0 +1,108 @@ +# = XmlMini ReXML implementation +module ActiveSupport + module XmlMini_REXML #:nodoc: + extend self + + CONTENT_KEY = '__content__'.freeze + + # Parse an XML Document string into a simple hash + # + # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, + # and uses the defaults from ActiveSupport + # + # string:: + # XML Document string to parse + def parse(string) + require 'rexml/document' unless defined?(REXML::Document) + doc = REXML::Document.new(string) + merge_element!({}, doc.root) + end + + private + # Convert an XML element and merge into the hash + # + # hash:: + # Hash to merge the converted element into. + # element:: + # XML element to merge into hash + def merge_element!(hash, element) + merge!(hash, element.name, collapse(element)) + end + + # Actually converts an XML document element into a data structure. + # + # element:: + # The document element to be collapsed. + def collapse(element) + hash = get_attributes(element) + + if element.has_elements? + element.each_element {|child| merge_element!(hash, child) } + merge_texts!(hash, element) unless empty_content?(element) + hash + else + merge_texts!(hash, element) + end + end + + # Merge all the texts of an element into the hash + # + # hash:: + # Hash to add the converted emement to. + # element:: + # XML element whose texts are to me merged into the hash + def merge_texts!(hash, element) + unless element.has_text? + hash + else + # must use value to prevent double-escaping + merge!(hash, CONTENT_KEY, element.texts.sum(&:value)) + end + end + + # Adds a new key/value pair to an existing Hash. If the key to be added + # already exists and the existing value associated with key is not + # an Array, it will be wrapped in an Array. Then the new value is + # appended to that Array. + # + # hash:: + # Hash to add key/value pair to. + # key:: + # Key to be added. + # value:: + # Value to be associated with key. + def merge!(hash, key, value) + if hash.has_key?(key) + if hash[key].instance_of?(Array) + hash[key] << value + else + hash[key] = [hash[key], value] + end + elsif value.instance_of?(Array) + hash[key] = [value] + else + hash[key] = value + end + hash + end + + # Converts the attributes array of an XML element into a hash. + # Returns an empty Hash if node has no attributes. + # + # element:: + # XML element to extract attributes from. + def get_attributes(element) + attributes = {} + element.attributes.each { |n,v| attributes[n] = v } + attributes + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def empty_content?(element) + element.texts.join.blank? + end + end +end |