libXML for Active Resource 2.0

Posted by Bart ten Brinke Mon, 25 Feb 2008 08:19:39 GMT

I received an email from Stevie Clifton today, asking about our libXML patch for rails 2.0. As we have been running 2.0 for quite some time now, I never realised I forgot to post the new overrides.

The file below goes into /config/initializers/libxml.rb

# This is actally a fix for activeresource as it
# will behave incorrectly when it encounters
# Complex xml files. This override fixes this,
# but it should be submitted to rails trunk.
module ActiveResource
  module Formats
    module XmlFormat
      private
      def from_xml_data(data)
          if data.is_a?(Hash) && data.keys.size == 1
            from_xml_data(data.values.first)
          else
            data
          end
        end      
    end
  end
end

module Nedap #:nodoc:
  module Hash #:nodoc:
    module Conversions

      def self.included(klass)
        require 'xml/libxml'
        klass.extend(ClassMethods)
      end

      module ClassMethods

        # Hash from_xml mixin that uses libxml.
        # This ensures a 20x speed increase
        # Compared to libxml. Plus it is less ugly.
        def from_xml(xml) 
          result = XML::Parser.string(xml).parse 
          return { result.root.name.to_s => xml_node_to_hash(result.root)} 
        end 

        def xml_node_to_hash(node) 
          # If we are at the root of the document, start the hash 
          if node.element? 
           if node.children? 
              result_hash = {} 

              node.each_child do |child| 
                result = xml_node_to_hash(child) 

              if child.name == "text"
                if !child.next? and !child.prev?
                  return result
                end
              elsif result_hash[child.name] 
                  if result_hash[child.name].is_a?(Object::Array) 
                    result_hash[child.name] << result 
                  else 
                    result_hash[child.name] = [result_hash[child.name]] << result 
                  end 
                else 
                  result_hash[child.name] = result 
                end              
              end 

              return result_hash 
            else 
              return nil 
           end 
           else 
            return node.content.to_s 
          end 
        end          

      end        
    end
  end
end 

Hash.send :include, Nedap::Hash::Conversions

There you go. You now have a blazingly fast active resource! If you want some more bang out of your resource, add the following mixins to the initializer too:

# Add inflate to NET class (zLib support)
module Net
  class HTTPResponse
     def inflate!
       require 'zlib'
       @body = Zlib::Inflate.inflate(@body)
     end
   end
 end

# Increase timeout and buffersize for big XML files
module Net
  class BufferedIO 
    def rbuf_fill
      timeout(3000) { 
        @rbuf << @io.sysread(32768) 
      }
    end 
  end
end

Posted in  | Tags , , ,  | no comments