Skip to content

Instantly share code, notes, and snippets.

@follesoe
Created May 22, 2012 16:57
Show Gist options
  • Save follesoe/2770242 to your computer and use it in GitHub Desktop.
Save follesoe/2770242 to your computer and use it in GitHub Desktop.
Simple Ruby XML DSL exercise implemented by Torgeir and Jonas

Simple Ruby DSL to generate XML

Implemented as an exercise to learn about metaprogramming in Ruby. The DSL is implemented using instance_eval and method_missing.

Usage

require 'xmldsl'

Twitter = Struct.new :name, :avatar, :text
twitters = []
5.times { twitters << Twitter.new("Jonas", "/profile.png", "Hello World!") }

xml = XML.generate do 
  html do    
    body do 
      twitters.each do |tw|
        img :src => tw.avatar, :alt => tw.name, :width => 25, :height => 25
        content [" : ", tw.text]
        br
        br        
      end
      form :action => '/page', :method => 'post' do
          input :type => 'text', :name => 'str', :maxlength => 3, :size => 3
          input :type => 'submit', :value => 'page'
      end      
    end
  end
end

open('sample_twitters.html', 'w'){|f| f.puts xml}
puts xml

Output

<html>
   <body >
      <img width='25' height='25' src='/profile.png' alt='TwitterHandle'/>
       : Hello Ruby DSL!
      <br />
      <br />
      <img width='25' height='25' src='/profile.png' alt='TwitterHandle'/>
       : Hello Ruby DSL!
      <br />
      <br />
      <img width='25' height='25' src='/profile.png' alt='TwitterHandle'/>
       : Hello Ruby DSL!
      <br />
      <br />
      <img width='25' height='25' src='/profile.png' alt='TwitterHandle'/>
       : Hello Ruby DSL!
      <br />
      <br />
      <img width='25' height='25' src='/profile.png' alt='TwitterHandle'/>
       : Hello Ruby DSL!
      <br />
      <br />
      <form action='/page' method='post'>
         <input type='text' maxlength='3' size='3' name='str'/>
         <input type='submit' value='page'/>
      </form>
   </body>
</html>

Implementation

class XML  
  def self.generate &block
    XML.new.xml_generate &block
  end

  def xml_generate &block
    instance_eval &block    
  end

  def initialize
    @xml = ""
    @indent = 0   
  end
 
  def method_missing name, *args, &block
    @xml += "#{add_indent}<#{name} #{attributes(*args)}"   
    if(block) 
      @xml += ">\n"
      @indent += 3
      block.call
      @indent -= 3
      @xml += "#{add_indent}</#{name}>\n"
    else
      @xml += "/>\n"
    end
  end

  def add_indent 
    " "*@indent 
  end

  def attributes *args
    (args[0] || {}).map { |key, value| "#{key}='#{value}'"}.join(" ")
  end

  def content txt 
    @xml += "#{add_indent}#{[txt].join}\n"
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment