require 'rubygems' require 'metaid' class XML # blank slate instance_methods.each { |m| undef_method m unless (m =~ /^__|instance_eval|meta/)} # Each item in @doc is an array containing three values: type, value, attributes def initialize(tag = nil, atts = nil, &block) @doc = '' @doc << _fmt_open(tag, atts) if tag block.call(self) if block @doc << _fmt_close(tag) if tag end def method_missing(tag, *args, &block) meta_def(tag) do |*args| @__to_s = nil # invalidate to_s cache if block @doc << _fmt_open(tag, args.first) block.call(self) if block @doc << _fmt_close(tag) else value, atts = args.pop, args.pop subtags, atts = atts, nil if atts.is_a?(Array) if subtags @doc << _fmt_open(tag, atts) subtags.each {|k| __send__(k, value[k])} @doc << _fmt_close(tag) else @doc << _fmt_open(tag, atts) @doc << _fmt_value(value) @doc << _fmt_close(tag) end end end __send__(tag, *args) end def instruct!(atts = nil) @doc.insert(0, _fmt_instruct(atts || {:version => "1.0", :encoding => "UTF-8"})) end def to_s @doc end alias_method :inspect, :to_s def _fmt_open(tag, atts) atts ? "<#{tag} #{_fmt_atts(atts)}>" : "<#{tag}>" end def _fmt_close(tag) "" end def _fmt_value(value) value.to_s end def _fmt_instruct(arg) "" end def _fmt_atts(atts) atts.inject([]) {|m, i| m << "#{i[0]}=#{i[1].to_s.inspect}"}.join(' ') end end def t1(list) xml = XML.new do |x| x.instruct! x.reality(:time => Time.now) do x.event do x.type 1 x.path "/sharon" x.value "abcdefg" end list.each {|i| x.item i} end end xml.to_s end puts t1([1, 3, 4]) puts "**************" count = 100000 list = (1..count).map { {:quality => rand(5), :value => rand(77865)} } t1 = Time.now xml = XML.new do |x| x.states do list.each {|s| x.state [:quality, :value], s} end end s1 = xml.to_s t3 = Time.now e1 = t3 - t1 r1 = count / e1 puts "build: #{e1} (#{r1})" puts "**************" t1 = Time.now xml = XML.new(:states) do |x| list.each {|s| x.state [:quality, :value], s} end s2 = xml.to_s t3 = Time.now e1 = t3 - t1 r1 = count / e1 puts "build: #{e1} (#{r1})" puts "bad result" unless s1 == s2 __END__ class EventsController < ServerSide::Controller::Base.mount('/events/:format') invoke_by_parameter :format, :default => :html def before_response @events = Events.select end def xml render_xml('text/xml+svg') do events @events.to_xml end end end 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 end xml.to_s