diff options
Diffstat (limited to 'activesupport/lib/active_support')
28 files changed, 309 insertions, 131 deletions
diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb index 6a41aab166..ee02ecb043 100644 --- a/activesupport/lib/active_support/benchmarkable.rb +++ b/activesupport/lib/active_support/benchmarkable.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/benchmark' +require 'active_support/core_ext/hash/keys' module ActiveSupport module Benchmarkable diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index f2d957f154..ad238c1d96 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/benchmark' require 'active_support/core_ext/exception' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/object/to_param' +require 'active_support/core_ext/string/inflections' module ActiveSupport # See ActiveSupport::Cache::Store for documentation. diff --git a/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb b/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb index d87eb17337..d2370d78c5 100644 --- a/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb @@ -1,3 +1,5 @@ +require 'active_support/gzip' + module ActiveSupport module Cache class CompressedMemCacheStore < MemCacheStore diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 1b6b820ca4..d584c9e254 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -1,4 +1,5 @@ require 'memcache' +require 'active_support/core_ext/array/extract_options' module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 5f6fe22416..86c7703c27 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/object/duplicable' +require 'active_support/core_ext/string/inflections' module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 05bc453dbf..8719dc0e3f 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/inheritable_attributes' require 'active_support/core_ext/kernel/reporting' +require 'active_support/core_ext/object/metaclass' module ActiveSupport # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic @@ -367,12 +368,6 @@ module ActiveSupport method << "halted ? false : (block_given? ? value : true)" method.compact.join("\n") end - - def clone(klass) - chain = CallbackChain.new(@name, @config.dup) - callbacks = map { |c| c.clone(chain, klass) } - chain.push(*callbacks) - end end module ClassMethods @@ -389,10 +384,16 @@ module ActiveSupport # key. See #define_callbacks for more information. # def __define_runner(symbol) #:nodoc: + send("_update_#{symbol}_superclass_callbacks") body = send("_#{symbol}_callbacks").compile(nil) body, line = <<-RUBY_EVAL, __LINE__ def _run_#{symbol}_callbacks(key = nil, &blk) + if self.class.send("_update_#{symbol}_superclass_callbacks") + self.class.__define_runner(#{symbol.inspect}) + return _run_#{symbol}_callbacks(key, &blk) + end + if key name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks" @@ -431,6 +432,8 @@ module ActiveSupport # CallbackChain. # def __update_callbacks(name, filters = [], block = nil) #:nodoc: + send("_update_#{name}_superclass_callbacks") + type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block @@ -470,7 +473,8 @@ module ActiveSupport def set_callback(name, *filter_list, &block) __update_callbacks(name, filter_list, block) do |chain, type, filters, options| filters.map! do |filter| - chain.delete_if {|c| c.matches?(type, filter) } + removed = chain.delete_if {|c| c.matches?(type, filter) } + send("_removed_#{name}_callbacks").push(*removed) Callback.new(chain, filter, type, options.dup, self) end @@ -482,16 +486,17 @@ module ActiveSupport # def skip_callback(name, *filter_list, &block) __update_callbacks(name, filter_list, block) do |chain, type, filters, options| - chain = send("_#{name}_callbacks=", chain.clone(self)) - filters.each do |filter| filter = chain.find {|c| c.matches?(type, filter) } if filter && options.any? - filter.recompile!(options, options[:per_key] || {}) - else - chain.delete(filter) + new_filter = filter.clone(chain, self) + chain.insert(chain.index(filter), new_filter) + new_filter.recompile!(options, options[:per_key] || {}) end + + chain.delete(filter) + send("_removed_#{name}_callbacks") << filter end end end @@ -499,7 +504,9 @@ module ActiveSupport # Reset callbacks for a given type. # def reset_callbacks(symbol) - send("_#{symbol}_callbacks").clear + callbacks = send("_#{symbol}_callbacks") + callbacks.clear + send("_removed_#{symbol}_callbacks").concat(callbacks) __define_runner(symbol) end @@ -546,14 +553,46 @@ module ActiveSupport # # Defaults to :kind. # - def define_callbacks(*symbols) - config = symbols.last.is_a?(Hash) ? symbols.pop : {} - symbols.each do |symbol| - extlib_inheritable_accessor("_#{symbol}_callbacks") do - CallbackChain.new(symbol, config) + def define_callbacks(*callbacks) + config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} + callbacks.each do |callback| + extlib_inheritable_reader("_#{callback}_callbacks") do + CallbackChain.new(callback, config) + end + + extlib_inheritable_reader("_removed_#{callback}_callbacks") do + [] end - __define_runner(symbol) + class_eval <<-METHOD, __FILE__, __LINE__ + 1 + def self._#{callback}_superclass_callbacks + if superclass.respond_to?(:_#{callback}_callbacks) + superclass._#{callback}_callbacks + superclass._#{callback}_superclass_callbacks + else + [] + end + end + + def self._update_#{callback}_superclass_callbacks + changed, index = false, 0 + + callbacks = (_#{callback}_superclass_callbacks - + _#{callback}_callbacks) - _removed_#{callback}_callbacks + + callbacks.each do |callback| + if new_index = _#{callback}_callbacks.index(callback) + index = new_index + 1 + else + changed = true + _#{callback}_callbacks.insert(index, callback) + index = index + 1 + end + end + changed + end + METHOD + + __define_runner(callback) end end end diff --git a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb b/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb index 1c3ef05526..d3c3575748 100644 --- a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +++ b/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb @@ -1,3 +1,5 @@ +require 'cgi' + class CGI #:nodoc: if RUBY_VERSION >= '1.9' def self.escape_skipping_slashes(str) diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb index 301c09fc73..72e0eefb0a 100644 --- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb @@ -1,5 +1,4 @@ require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/array/extract_options' class Class diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index e4d22516c1..2b8e2b544f 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -159,7 +159,7 @@ class Class # (error out or do the same as other methods above) instead of silently # moving on). In particular, this makes the return value of this function # less useful. - def extlib_inheritable_reader(*ivars) + def extlib_inheritable_reader(*ivars, &block) options = ivars.extract_options! ivars.each do |ivar| @@ -178,6 +178,7 @@ class Class end RUBY end + instance_variable_set(:"@#{ivar}", yield) if block_given? end end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 2b76b93153..9d2ad2bbcf 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -1,6 +1,7 @@ require 'date' require 'active_support/duration' require 'active_support/core_ext/time/zones' +require 'active_support/core_ext/object/acts_like' class Date class << self diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb index e93abfa4a3..26979aa906 100644 --- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb @@ -1,4 +1,5 @@ require 'rational' unless RUBY_VERSION >= '1.9.2' +require 'active_support/core_ext/object/acts_like' class DateTime class << self diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 35ccec5df4..cfd840fb93 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,6 +1,8 @@ require 'active_support/time' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/reverse_merge' +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/string/inflections' class Hash # This module exists to decorate files deserialized using Hash.from_xml with diff --git a/activesupport/lib/active_support/core_ext/module/loading.rb b/activesupport/lib/active_support/core_ext/module/loading.rb index 4b4b110b25..43d0578ae6 100644 --- a/activesupport/lib/active_support/core_ext/module/loading.rb +++ b/activesupport/lib/active_support/core_ext/module/loading.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/inflections' + class Module # Returns String#underscore applied to the module name minus trailing classes. # diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb index f72d512340..115b8abd4e 100644 --- a/activesupport/lib/active_support/core_ext/module/synchronization.rb +++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/array/extract_options' class Module # Synchronize access around a method, delegating synchronization to a diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb index de8121f274..b8b6970382 100644 --- a/activesupport/lib/active_support/core_ext/object/extending.rb +++ b/activesupport/lib/active_support/core_ext/object/extending.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/class/removal' +require 'active_support/core_ext/object/blank' + class Class # Rubinius if defined?(Class.__subclasses__) diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index 7ca763cbad..49e41e919a 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -1,3 +1,5 @@ + + class Object # Alias of <tt>to_s</tt>. def to_param diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index deb29a82b8..cec8024b17 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/array/extract_options' module ActiveSupport class << Deprecation diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 713ae1b671..c1f0e4bf81 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,5 +1,6 @@ require 'active_support/basic_object' require 'active_support/core_ext/array/conversions' +require 'active_support/core_ext/object/acts_like' module ActiveSupport # Provides accurate date and time measurements using Date#advance and diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 347af9dc76..51fa626b45 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -1,4 +1,5 @@ require 'openssl' +require 'active_support/base64' module ActiveSupport # MessageEncryptor is a simple way to encrypt values which get stored somewhere diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 87e4b1ad33..6c46b68eaf 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -1,3 +1,6 @@ +require 'active_support/base64' +require 'active_support/core_ext/object/blank' + module ActiveSupport # MessageVerifier makes it easy to generate and verify messages which are signed # to prevent tampering. diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index f0119f5994..6e660f8647 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -1,5 +1,7 @@ require 'active_support/core_ext/class/inheritable_attributes' require 'active_support/core_ext/proc' +require 'active_support/core_ext/string/inflections' +require 'active_support/core_ext/array/extract_options' module ActiveSupport # Rescuable module adds support for easier exception handling. diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 66e32fa5d7..24eea1e40b 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -4,6 +4,7 @@ begin require 'fileutils' require 'rails/version' require 'active_support/core_ext/class/delegating_attributes' + require 'active_support/core_ext/string/inflections' module ActiveSupport module Testing diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 8304f6c434..6b554e7158 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -1,4 +1,5 @@ require "active_support/values/time_zone" +require 'active_support/core_ext/object/acts_like' module ActiveSupport # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index 5c3d93c4a1..48c1cb3fe9 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -3,6 +3,8 @@ raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFO require 'jruby' include Java +require 'active_support/core_ext/object/blank' + import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory import java.io.StringReader unless defined? StringReader diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 0f7ba1918b..9cf187302f 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -1,4 +1,6 @@ require 'libxml' +require 'active_support/core_ext/object/returning' +require 'active_support/core_ext/object/blank' # = XmlMini LibXML implementation module ActiveSupport @@ -12,7 +14,7 @@ module ActiveSupport if !data.respond_to?(:read) data = StringIO.new(data || '') end - + char = data.getc if char.nil? {} @@ -34,106 +36,42 @@ module LibXML #:nodoc: end module Node #:nodoc: - CONTENT_ROOT = '__content__' - LIB_XML_LIMIT = 30000000 # Hardcoded LibXML limit + CONTENT_ROOT = '__content__'.freeze # Convert XML document to hash # # hash:: # Hash to merge the converted element into. def to_hash(hash={}) - if text? || cdata? - raise LibXML::XML::Error if hash[CONTENT_ROOT].to_s.length + content.length >= LIB_XML_LIMIT - hash[CONTENT_ROOT] = hash[CONTENT_ROOT].to_s + 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 + node_hash = {} - 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 + # Insert node hash into parent hash correctly. + case hash[name] + when Array then hash[name] << node_hash + when Hash then hash[name] = [hash[name], node_hash] + when nil then hash[name] = node_hash end - # Insert children into hash - # - # hash:: - # Hash to merge the children into. - def children_to_hash(hash={}) - each { |child| child.to_hash(hash) } - - if hash.length > 1 && hash[CONTENT_ROOT].blank? - hash.delete(CONTENT_ROOT) + # Handle child elements + each_child do |c| + if c.element? + c.to_hash(node_hash) + elsif c.text? || c.cdata? + node_hash[CONTENT_ROOT] ||= '' + node_hash[CONTENT_ROOT] << c.content end - - 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 + # Remove content node if it is blank + if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank? + node_hash.delete(CONTENT_ROOT) 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 + # Handle attributes + each_attr { |a| node_hash[a.name] = a.value } + hash + end end end end diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb new file mode 100644 index 0000000000..d7b2f4c5be --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -0,0 +1,84 @@ +require 'libxml' + +# = XmlMini LibXML implementation using a SAX-based parser +module ActiveSupport + module XmlMini_LibXMLSAX + extend self + + # Class that will build the hash while the XML document + # is being parsed using SAX events. + class HashBuilder + + include LibXML::XML::SaxParser::Callbacks + + CONTENT_KEY = '__content__'.freeze + HASH_SIZE_KEY = '__hash_size__'.freeze + + attr_reader :hash + + def current_hash + @hash_stack.last + end + + def on_start_document + @hash = { CONTENT_KEY => '' } + @hash_stack = [@hash] + end + + def on_end_document + @hash = @hash_stack.pop + @hash.delete(CONTENT_KEY) + end + + def on_start_element(name, attrs = {}) + new_hash = { CONTENT_KEY => '' }.merge(attrs) + new_hash[HASH_SIZE_KEY] = new_hash.size + 1 + + case current_hash[name] + when Array then current_hash[name] << new_hash + when Hash then current_hash[name] = [current_hash[name], new_hash] + when nil then current_hash[name] = new_hash + end + + @hash_stack.push(new_hash) + end + + def on_end_element(name) + if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == '' + current_hash.delete(CONTENT_KEY) + end + @hash_stack.pop + end + + def on_characters(string) + current_hash[CONTENT_KEY] << string + end + + alias_method :on_cdata_block, :on_characters + end + + attr_accessor :document_class + self.document_class = HashBuilder + + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || '') + end + + char = data.getc + if char.nil? + {} + else + data.ungetc(char) + + LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER) + parser = LibXML::XML::SaxParser.io(data) + document = self.document_class.new + + parser.callbacks = document + parser.parse + document.hash + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 17bacd8441..eb61a7fc22 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -1,4 +1,5 @@ require 'nokogiri' +require 'active_support/core_ext/object/blank' # = XmlMini Nokogiri implementation module ActiveSupport @@ -12,13 +13,13 @@ module ActiveSupport if !data.respond_to?(:read) data = StringIO.new(data || '') end - + char = data.getc if char.nil? {} else data.ungetc(char) - doc = Nokogiri::XML(data) { |cfg| cfg.noblanks } + doc = Nokogiri::XML(data) raise doc.errors.first if doc.errors.length > 0 doc.to_hash end @@ -32,39 +33,41 @@ module ActiveSupport end module Node #:nodoc: - CONTENT_ROOT = '__content__' + CONTENT_ROOT = '__content__'.freeze # Convert XML document to hash # # hash:: # Hash to merge the converted element into. - def to_hash(hash = {}) - attributes = attributes_as_hash - if hash[name] - hash[name] = [hash[name]].flatten - hash[name] << attributes - else - hash[name] ||= attributes - end + def to_hash(hash={}) + node_hash = {} - children.each { |child| - next if child.blank? && 'file' != self['type'] + # Insert node hash into parent hash correctly. + case hash[name] + when Array then hash[name] << node_hash + when Hash then hash[name] = [hash[name], node_hash] + when nil then hash[name] = node_hash + end - if child.text? || child.cdata? - (attributes[CONTENT_ROOT] ||= '') << child.content - next + # Handle child elements + children.each do |c| + if c.element? + c.to_hash(node_hash) + elsif c.text? || c.cdata? + node_hash[CONTENT_ROOT] ||= '' + node_hash[CONTENT_ROOT] << c.content end + end - child.to_hash attributes - } + # Remove content node if it is blank and there are child tags + if node_hash.length > 1 && node_hash[CONTENT_ROOT].blank? + node_hash.delete(CONTENT_ROOT) + end - hash - end + # Handle attributes + attribute_nodes.each { |a| node_hash[a.node_name] = a.value } - def attributes_as_hash - Hash[*(attribute_nodes.map { |node| - [node.node_name, node.value] - }.flatten)] + hash end end end diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb new file mode 100644 index 0000000000..d538a9110f --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -0,0 +1,82 @@ +require 'nokogiri' + +# = XmlMini Nokogiri implementation using a SAX-based parser +module ActiveSupport + module XmlMini_NokogiriSAX + extend self + + # Class that will build the hash while the XML document + # is being parsed using SAX events. + class HashBuilder < Nokogiri::XML::SAX::Document + + CONTENT_KEY = '__content__'.freeze + HASH_SIZE_KEY = '__hash_size__'.freeze + + attr_reader :hash + + def current_hash + @hash_stack.last + end + + def start_document + @hash = {} + @hash_stack = [@hash] + end + + def end_document + raise "Parse stack not empty!" if @hash_stack.size > 1 + end + + def error(error_message) + raise error_message + end + + def start_element(name, attrs = []) + new_hash = { CONTENT_KEY => '' } + new_hash[attrs.shift] = attrs.shift while attrs.length > 0 + new_hash[HASH_SIZE_KEY] = new_hash.size + 1 + + case current_hash[name] + when Array then current_hash[name] << new_hash + when Hash then current_hash[name] = [current_hash[name], new_hash] + when nil then current_hash[name] = new_hash + end + + @hash_stack.push(new_hash) + end + + def end_element(name) + if current_hash.length > current_hash.delete(HASH_SIZE_KEY) && current_hash[CONTENT_KEY].blank? || current_hash[CONTENT_KEY] == '' + current_hash.delete(CONTENT_KEY) + end + @hash_stack.pop + end + + def characters(string) + current_hash[CONTENT_KEY] << string + end + + alias_method :cdata_block, :characters + end + + attr_accessor :document_class + self.document_class = HashBuilder + + def parse(data) + if !data.respond_to?(:read) + data = StringIO.new(data || '') + end + + char = data.getc + if char.nil? + {} + else + data.ungetc(char) + document = self.document_class.new + parser = Nokogiri::XML::SAX::Parser.new(document) + parser.parse(data) + document.hash + end + end + end +end
\ No newline at end of file |