Implemented as an exercise to learn about metaprogramming in Ruby. The DSL is implemented using instance_eval
and method_missing
.
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
<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>
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