class XML # Create an instance of this class, specifying a stream or object to # hold the output. This can be any object that responds to <<(String). def initialize(out) @out = out # Remember where to send our output end # Output the specified object as CDATA, return nil. def content(text) @out << text.to_s nil end # Output the specified object as a comment, return nil. def comment(text) @out << "" nil end # Output a tag with the specified name and attributes. # If there is a block invoke it to output or return content. # Return nil. def tag(tagname, attributes={}) # Output the tag name @out << "<#{tagname}" # Output the attributes attributes.each {|attr,value| @out << " #{attr}='#{value}'" } if block_given? # This block has content @out << '>' # End the opening tag content = yield # Invoke the block to output or return content if content # If any content returned @out << content.to_s # Output it as a string end @out << "" # Close the tag else # Otherwise, this is an empty tag, so just close it. @out << '/>' end nil # Tags output themselves, so they don't return any content end # The code below is what changes this from an ordinary class into a DSL. # First: any unknown method is treated as the name of a tag. alias method_missing tag # Second: run a block in a new instance of the class. def self.generate(out, &block) XML.new(out).instance_eval(&block) end end