Never been to DZone Snippets before?

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

A Fast Ruby XML Builder (See related posts)

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. 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 on this post

jmweir posts on Oct 11, 2006 at 22:45
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 ) ) + "</#{part[1]}>\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

You need to create an account or log in to post comments to this site.


Click here to browse all 5059 code snippets

Related Posts