Tuesday, September 18th, 2007...11:42 am
Easy XML Generation with Ruby and ERB
Ah, the bracket tax. XML-Situps, whatever you want to call them, if you write any code (especially if you use java) it should be your sworn enemy. The other day I had to create about 40 XML files for some Java Webstart configs. Instead of doing them by hand, I decided to check out the ERB class for easy "templating" that's built into Ruby. It makes doing code generation very easy!
Our Task
Create a custom XML file for each .jar file in a directory.
...more after the jump!
Strategy
With any code generation project we need to identify a few things.
- Static template: The code/text that will stay the same.
- Dynamic variables: These are the pieces that will change in our template.
- Input source: The input for our dynamic variables.
For me, the static template is a JNLP XML file that looks like this:
<jnlp
spec="1.0+"
codebase="http://redacted.com/libs/com.redacted.dao_1.3"
href="RcpWebStart.jnlp">
<information>
<title>RedactedLibs</title>
<vendor>Redacted</vendor>
<description><strong>com.redacted.dao_1.3.1.jar</strong></description>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.4+"/>
<jar href="com.redacted.dao_1.3.1.jar"/>
</resources>
<component-desc/>
</jnlp>
The dynamic bits will be anywhere a jar is referenced as well as the directory for the jar.
The Template
The template defines our static text, and the variables and/or ruby code that will be substituted into the text by ERB.
We use
blocks to substitute in variables from our ruby script.
Here's what my template looks like, I have four variables that I want to substitute in.
- basedir: the base directory of where I'm storing my jar.
- shortname: the name of the jar sans the version.
- jarname: the full name of the jar file.
<jnlp
spec="1.0+"
codebase="http://redacted.net/libs/<%=basedir%>/<%=shortname%>_<%=version%>"
href="RcpWebStart.jnlp">
<information>
<title><%=basedir%></title>
<vendor>Redacted</vendor>
<description><%=jarname%></description>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.4+"/>
<jar href="<%=jarname%>"/>
</resources>
<component-desc/>
</jnlp>
The Code
Let's examine some code. The first thing I do is create the template string, i Use the %q{ shortcut to avoid having to worry about quotes.
Next, I need to define my template, that's what this line does.
It says, create a new ERB object using the string 'template' as the template and the "%<>" as my template escape operators.
Our next step is to loop through all the jar's in a directory and create a new file for each one, here's the juicy bits to accomplish that.
jarname = File.basename(path)
basedir = ARGV[1]
shortname = jarname.gsub(/(.+?)_(\d.+)$/,'\1')
version = jarname.gsub(/(.+?)_(\d\.\d)(.+)$/,'\2')
dirname = shortname + "_" + version
ourfile = jnlp.result(b)
Notice that we create a binding variable called b and then pass that to our ERB object (jnlp), that binding variable tells ERB to use template variables in the same scope as it. It's some ruby black magic that makes our lives easier.
The rest of the script actually creates some directory and writes the created file to a directory based on the name of the jar.
require "find"
template = %q{
<?xml version="1.0" encoding="UTF-8"?>
<jnlp
spec="1.0+"
codebase="http://redacted.net/libs/<%=basedir%>/<%=shortname%>_<%=version%>"
href="RcpWebStart.jnlp">
<information>
<title><%=basedir%></title>
<vendor>Redactedl</vendor>
<description><%=jarname%></description>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.4+"/>
<jar href="<%=jarname%>"/>
</resources>
<component-desc/>
</jnlp>}
jnlp = ERB.new(template, 0, "%<>");
#Search for jar files here
Find.find(ARGV[0]) do |path|
if !FileTest.directory?(path)
b = binding
jarname = File.basename(path)
basedir = ARGV[1]
shortname = jarname.gsub(/(.+?)_(\d.+)$/,'\1')
version = jarname.gsub(/(.+?)_(\d\.\d)(.+)$/,'\2')
dirname = shortname + "_" + version
#pass in the binding variable and create a string from the template
ourfile = jnlp.result(b)
#create a new directory and file, and output the template string to it.
Dir.mkdir(basedir + "/" + dirname)
outfile = File.new(basedir + "/" + dirname + "/RcpWebStart.jnlp","w+");
outfile <<ourfile
end
The fun doesn't stop there
This is an elementary example, but ERB also allows you to embed real ruby code in the template, this allows you to accomplish looping.
So we could pass in an array of person objects and loop through them with a template like this:
<%@people.each do |person| %>
<name><%=person.name%></name>
<%end%>
</people>






1 Comment
April 15th, 2009 at 2:17 pm
Not that I'm impressed a lot, but this is a lot more than I expected when I stumpled upon a link on Furl telling that the info is awesome. Thanks.
Leave a Reply