DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

A Fast Ruby XML Builder

09.04.2006
| 9829 views |
  • submit to reddit
        Here's something I wrote this evening. It's a simple and fast XML builder. Some benchmark I ran show it to be about 4 times faster than <a>Builder</a>. Usage example:

items = ['abc', 'def', 'ghi']
xml = XML.new do
  instruct!
  reality(:time => Time.now) do
  event do
    type 1
    path "/sharon"
    value "abcdefg"
  end
  list.each {|i| item i}
end
puts xml.to_s

The XML class:

class XML
  # blank slate
  instance_methods.each { |m| undef_method m unless (m =~ /^__|instance_eval$/)}
  
  # Each item in @doc is an array containing three values: type, value, attributes
  
  def initialize(&block)
    @doc = []
    instance_eval(&block)
  end
  
  def method_missing(tag, *args, &block)
    value = block ? nil : args.pop
    attributes = args.pop
    @__to_s = nil # invalidate to_s cache
    if value
      @doc << [:open, tag, attributes] << [:value, value] << [:close, tag]
    else
      @doc << [:open, tag, attributes]
      instance_eval(&block)
      @doc << [:close, tag]
    end
  end
  
  def instruct!(atts = nil)
    @doc << [:instruct, atts || {:version => "1.0", :encoding => "UTF-8"}]
  end

  def to_s
    @__to_s ||= @doc.map{|i| _fmt_part i}.join
  end
  
  alias_method :inspect, :to_s
  
  def _fmt_part(part)
    case part[0]
    when :instruct
      "<?xml #{_fmt_atts(part[1])}?>"
    when :open
      part[2] ? "<#{part[1]} #{_fmt_atts(part[2])}>" :
        "<#{part[1]}>"
    when :close
      "</#{part[1]}>"
    when :value
      part[1].to_s
    end
  end

  def _fmt_atts(atts)
    atts.inject([]) {|m, i| m << "#{i[0]}=#{i[1].to_s.inspect}"}.join(' ')
  end
end
    

Comments

Snippets Manager replied on Thu, 2010/05/27 - 3:59pm

I see a 6 to 10 fold increase in performance compared to default rails 2.3.5 xml builder, congratz, very well done. Greets, Chris

Snippets Manager replied on Fri, 2009/10/02 - 9:46am

A Fast Ruby XML Builder is a simple and fast XML builder. ... little ruby on rails plugin containing a form builder that uses css to produce tableless website marketing

Jim Weir replied on Wed, 2006/10/11 - 10:41pm

Here's a "pretty print" version of the XML class: class XML instance_methods.each { |m| undef_method m unless ( m =~ /^__|instance_eval$/) } def initialize( indent = 2, &block ) @doc = [] @indent = indent @level = 0 instance_eval( &block ) end def method_missing( tag, *args, &block ) value = block ? nil : args.pop attributes = args.pop @__to_s = nil if value @doc << [ :open, tag, attributes, @level ] << [ :value, value, @level + 1 ] << [ :close, tag, @level ] else @doc << [ :open, tag, attributes, @level ] @level += 1 instance_eval( &block ) @level -= 1 @doc << [ :close, tag, @level ] end end def instruct!( atts = nil ) @doc << [ :instruct, atts || { :version => "1.0", :encoding => "UTF-8" } ] end def to_s @__to_s ||= @doc.map{ |i| _fmt_part i }.join end alias_method :inspect, :to_s def _fmt_part( part ) case part[0] when :instruct "<?xml #{_fmt_atts( part[1] )} ?>\n" when :open part[2] ? ( " " * ( @indent * part[3].to_i ) ) + "<#{part[1]} #{_fmt_atts(part[2])}>\n" : ( " " * ( @indent * part[3].to_i ) ) + "<#{part[1]}>\n" when :close ( " " * ( @indent * part[2].to_i ) ) + "\n" when :value ( " " * ( @indent * part[2].to_i ) ) + part[1].to_s + "\n" end end def _fmt_atts( atts ) atts.inject([]) { |m, i| m << "#{i[0]}=#{i[1].to_s.inspect}" }.join(' ') end end