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
A Fast Ruby XML Builder
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
Snippets Manager replied on Fri, 2009/10/02 - 9:46am
Jim Weir replied on Wed, 2006/10/11 - 10:41pm
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