<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>ananelson.com</title>
  <subtitle>she who scrivens</subtitle>
  <link href="http://ananelson.com/feeds/blog.xml" rel="self" />
  <link href="http://ananelson.com/" />
  <updated>2009-09-23T21:10:28+01:00</updated>
  <author>
    <name>Ana Nelson</name>
  </author>
  <id>http://ananelson.com/</id>
  <entry>
      <title>PLEASE UPDATE YOUR FEED</title>
      <link href="/feeds/blog.xml" />
      <id>tag:ananelson.com,2009=09-23:1253707597</id>
      <updated>2009-09-23T15:06:36+01:00</updated>
      <content type="html">
          &lt;p&gt;This feed has moved. If you'd like to keep reading, please move to &lt;a href="http://ananelson.com/feeds/blog.xml"&gt;the new feed&lt;/a&gt;&lt;/p&gt;
      </content>
  </entry>
  <entry>
    <title>A DIY HTML R Transcript Viewer</title>
    <link href="/blog/2009/09/a-diy-html-r-transcript-viewer/" />
    <id>tag:ananelson.com,2009-09-23:1253707596</id>
    <updated>2009-09-23T13:06:36+01:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;This is a fun and very useful little script which I wrote today that I want to share. It generates a HTML file &lt;strong&gt;with embedded images&lt;/strong&gt; of the results of running an R script. So, you have a little R script:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# http://addictedtor.free.fr/graphiques/RGraphGallery.php?graph=63&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;palette&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rainbow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; s &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; v &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;quartz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;stars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;mtcars&lt;span class=&quot;p&quot;&gt;[,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;:&lt;span class=&quot;m&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; len &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; key.loc &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
           main &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Motor Trend Cars&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; draw.segments &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
And you want to run this and share the output as HTML. Well, running this command:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;rout2html tiny-example.R
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;
will evaluate the script in R, build a very pretty HTML file and include all the images. &lt;a href=&quot;tiny-example.R-with-images.html&quot;&gt;Check it out!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll get to the fun stuff of how this is implemented soon, but first a little about why this is useful. Have you heard the song &lt;a href=&quot;http://biostat.jhsph.edu/courses/bio623/misc/poetry2007.htm&quot;&gt;Baby Got Stats&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;&lt;embed src= &quot;http://www.odeo.com/flash/audio_player_standard_gray.swf&quot; quality=&quot;high&quot; width=&quot;300&quot; height=&quot;52&quot; allowScriptAccess=&quot;always&quot; wmode=&quot;transparent&quot;  type=&quot;application/x-shockwave-flash&quot; flashvars= &quot;valid_sample_rate=true&amp;external_url=http://www.biostat.jhsph.edu/courses/bio623/misc/baby_got_stats.mp3&quot; pluginspage=&quot;http://www.macromedia.com/go/getflashplayer&quot;&gt; &lt;/embed&gt;
&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Well, if you&amp;#8217;re a born-again convert to the command line like me, the first word that&amp;#8217;s going to jump out at you from this song is &amp;#8220;logfile&amp;#8221;. Logfile? A logfile means you are working interactively in a GUI and then later you are going to go back through the logfile to extract the relevant results. This is not a good idea. It means your results are not easily &lt;a href=&quot;http://en.wikipedia.org/wiki/Reproducibility&quot;&gt;reproducible&lt;/a&gt;. This is bad for you if you need to change something or want to double-check that you&amp;#8217;ve done your calculations correctly. This is bad for anyone else if they want to check or review your results. A better approach is to write a script and revise that script until it gives you the output you are looking for. That&amp;#8217;s reproducible, testable, documentable, and easily sharable.&lt;/p&gt;
&lt;p&gt;The problem with GUI apps is that they encourage you to take a logfile approach, rather than a batchfile approach. When you fire up the R GUI, the most natural thing you can do is to start typing. Resist! GUIs should be used as a scratchpad or for interactive help. Instead open your favourite text editor, type up a script (your text editor&amp;#8217;s syntax highlighting is going to be MUCH nicer than the R GUI&amp;#8217;s), run it using rout2html and view the results in your web browser. Make changes to the script, run the command again, refresh your browser. Now iterate. This approach works in Windows too! (Well, in principle this approach works in Windows. For now rout2html is a bash script. I&amp;#8217;ll look into writing a Windows .bat file or making it system-independent.)&lt;/p&gt;
&lt;p&gt;There are other applications for rout2html. You can use this to automate a remote R script and view the results in HTML format, for instance if you wanted to analyze performance of a web application. (Check out &lt;a href=&quot;http://www.splunk.com&quot;&gt;splunk&lt;/a&gt; if you need to parse server logs.) If for some reason you were trying to use R but couldn&amp;#8217;t run a GUI, like on a remote server, then you could use rout2html to generate HTML transcripts and view them via a web browser.&lt;/p&gt;
&lt;p&gt;So, how does this work? Well, rout2html is a bash script which basically does 3 things:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Runs your R script in R via R CMD BATCH.&lt;/li&gt;
	&lt;li&gt;Highlights the resulting Rout file using &lt;a href=&quot;http://pygments.org&quot;&gt;Pygments&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Looks for special image codes in the HTML file and replaces them with HTML image tags.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately the R Console transcript highlighter has just been submitted to Pygments, so it&amp;#8217;s not available in the latest released version. I&amp;#8217;ll update this post if &lt;a href=&quot;http://dev.pocoo.org/projects/pygments/ticket/439&quot;&gt;my patch&lt;/a&gt; gets accepted. In the mean time you&amp;#8217;ll need to download the source to Pygments, apply my patch and run &lt;code&gt;setup.py install&lt;/code&gt; or &lt;code&gt;setup.py develop&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order to get images to show up, you&amp;#8217;ll need to have a call to &lt;code&gt;quartz()&lt;/code&gt; or some other supported graphics device in your R script before each plot. If you&amp;#8217;ve been working with the GUI a lot, you probably have that in there already anyway. rout2html runs a script called &lt;code&gt;redefine-graphics-devices.R&lt;/code&gt; before it calls your script:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;quartz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;png&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; random_filename_generator&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;png&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;X11&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;quartz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;pdf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; f&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;quartz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;jpeg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; f&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;quartz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

random_filename_generator &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;file_extension&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    random_filename &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;paste&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;runif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; file_extension&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; sep&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;file_extension&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;_filename:&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; random_filename&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;\n&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; sep&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;random_filename&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This basically hijacks the graphics devices so they all generate a PNG file with a randomly generated filename, and so the R transcript contains a reference to this PNG filename. The 3rd step in rout2html looks for these filename references and converts them to HTML img tags so the final HTML actually contains the images. The nice thing about this is that, assuming you have explicitly called a graphics device before you start plotting to it, you don&amp;#8217;t have to change a thing. You can go back and forth between running this script in the GUI via source(), or via R CMD BATCH yourself, or via rout2html, and it will work in all 3 cases. If you have specified a filename for a PDF or JPEG device, this will be ignored when run via rout2html. The only issue will arise with the PNG device. Because this device is not overwritten (for now) if you have called the png() device, then you&amp;#8217;ll need to cat the filename manually in the same format as the random_filename_generator() function, e.g. &lt;code&gt;png_filename:my-custom-filename.png&lt;/code&gt;, followed by a newline. Then an img tag should be created for you.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;png&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;i-want-this-filename.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;png_filename:i-want-this-filename.png\n&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;png-filename-example.R-with-images.html&quot;&gt;Take a look at the output.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So, in order to run this you need a modified Pygments, the rout2html script somewhere on your PATH, and the &lt;code&gt;redefine-graphics-devices.R&lt;/code&gt; script in the same directory as your R script. My plan is to eventually have this implemented as an R package + an executable script, so that R can look after making redefine-graphics-devices available. For now, if you are skilled enough to get this working then you are probably skilled enough to modify it for your needs. Also, all HTML and PNG images will be created in the directory with your R script and redefine-graphics-devices.R. You are welcome to move the HTML and PNG after they are generated, but keep them together since the image tags just reference the filename. CSS is bundled into the header of the HTML. If you wanted to rewrite this script to use a syntax highlighting library other than Pygments, then in addition to changing the &lt;code&gt;pygmentize&lt;/code&gt; command, you would need to change the Ruby regexp based on what the output line would look like.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Save any old workspace.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -e &lt;span class=&quot;s2&quot;&gt;&amp;quot;.RData.xyztmp&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;A file named .RData.xyztmp already exists. Please remove this before proceeding.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -e &lt;span class=&quot;s2&quot;&gt;&amp;quot;.RData&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;    &lt;/span&gt;mv .RData .RData.xyztmp
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Execute redefine-graphics-devices.R and save results in fresh workspace.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Don&amp;#39;t want an outfile here.&lt;/span&gt;
R CMD BATCH --no-restore --save redefine-graphics-devices.R /dev/null

&lt;span class=&quot;c&quot;&gt;# Now run the R script passed in as $1, &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# which will have access to anything defined in our new workspace.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# $2 is passed to the R script as an argument, &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# this will just be ignored unless you look for it,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# but it gives you the option to pass args to your script.&lt;/span&gt;

R CMD BATCH --restore --no-save &lt;span class=&quot;s2&quot;&gt;&amp;quot;--args $2&amp;quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1out&lt;/span&gt;
rm .RData 

&lt;span class=&quot;c&quot;&gt;# Syntax highlight and create HTML file using Pygments&lt;/span&gt;
pygmentize -o &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;.html -O full &lt;span class=&quot;nv&quot;&gt;$1out&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Now look for png_filename: or jpg_filename: and convert to HTML img tags.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RUBY_CMD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;gsub(/&amp;lt;span class=\&amp;quot;go\&amp;quot;&amp;gt;(png|jpg)_filename:([0-9\.A-Za-z-]+\.(png|jpg))&amp;lt;\/span&amp;gt;/, &amp;#39;&amp;lt;img src=\&amp;quot;\2\&amp;quot; \/&amp;gt;&amp;#39;)&amp;quot;&lt;/span&gt; 
ruby -pe &lt;span class=&quot;s2&quot;&gt;&amp;quot;$RUBY_CMD&amp;quot;&lt;/span&gt; &amp;lt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;.html &amp;gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;-with-images.html

&lt;span class=&quot;c&quot;&gt;# Restore any old workspace.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; -e &lt;span class=&quot;s2&quot;&gt;&amp;quot;.RData.xyztmp&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;    &lt;/span&gt;mv .RData.xyztmp .RData
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
    </content>
  </entry>
  
  <entry>
    <title>OSSBarCamp Talk on Automated Documentation</title>
    <link href="/blog/2009/09/ossbarcamp-talk-on-automated-documentation/" />
    <id>tag:ananelson.com,2009-09-20:1253466979</id>
    <updated>2009-09-20T18:16:19+01:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;You can download PDFs of the handout from my talk at the &lt;a href=&quot;http://www.ossbarcamp.com&quot;&gt;September 2009 OSSBarCamp&lt;/a&gt; in the sidebar. Unfortunately the hyperlinks aren&amp;#8217;t live in the PDFs, so here are the software projects I mentioned in my talk. The outline was done using &lt;a href=&quot;http://freemind.sourceforge.net&quot;&gt;FreeMind&lt;/a&gt; which I&amp;#8217;ve just started playing around with.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://webby.rubyforge.org&quot;&gt;Webby&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Webby is primarily a tool for building static websites, but its combination of Rake and a generic filter/helper structure makes it phenominally powerful for managing any kind of collection of documents. I use Webby as the glue for my Automated Documentation &amp;#8220;stack&amp;#8221;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://gorgyrella.rubyforge.org&quot;&gt;Gorgyrella&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Gorgyrella is my Ruby port of the Python library Idiopidae. Gorgyrella lets you add special comments to your source code to define sections, which you can then pull out to include in documenation or to execute with Dobby.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://dobby.rubyforge.org&quot;&gt;Dobby&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dobby is still in its infancy, but its goal is to allow you to run snippets of code in an interpreter and display the output in a document. What makes it especially convenient for writing tutorials is that you can keep a Dobby session open, run a setup script invisibly, and only show the output of running a few lines of script at a time but with the memory of any previous commands still intact. Dobby currently supports R and Python, but in principle can be adapted to support any software with a command line interface.&lt;/p&gt;
&lt;p&gt;For syntax highlighting, I prefer &lt;a href=&quot;http://pygments.org&quot;&gt;Pygments&lt;/a&gt; but Gorgyrella also has support for &lt;a href=&quot;http://ultraviolet.rubyforge.org&quot;&gt;UltraViolet&lt;/a&gt; and &lt;a href=&quot;http://coderay.rubychan.de&quot;&gt;CodeRay&lt;/a&gt;. It is very easy to add support for other syntax highlighting packages.&lt;/p&gt;
&lt;p&gt;The projects which use Webby+Gorgyrella for their documentation are:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://surpass.rubyforge.org&quot;&gt;Surpass&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Write native Excel files using just pure Ruby. This one has the screenshot automation.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://bloombergapi.com/rbloomberg&quot;&gt;RBloomberg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Interface between R + Bloomberg financial data.&lt;/p&gt;
&lt;p&gt;Specifically for &lt;a href=&quot;http://www.r-project.org&quot;&gt;Arrrrrrrrrr&lt;/a&gt; users&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, the &lt;a href=&quot;http://sunsite.univie.ac.at/rcom/&quot;&gt;rcom&lt;/a&gt; server allows integration with R and various Windows packages which can be scripted using COM/OLE. In particular, &lt;a href=&quot;http://cran.r-project.org/web/packages/R2PPT/index.html&quot;&gt;R2PPT&lt;/a&gt; integrates R with PowerPoint.&lt;/p&gt;
&lt;p&gt;I currently use &lt;a href=&quot;http://bazaar-vcs.org&quot;&gt;Bazaar&lt;/a&gt; for version control. It&amp;#8217;s a distributed version control system and very easy to install and get started with. Once you have it installed if you want to check out the source code for Surpass to see how the documentation is compiled, then you just need to type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
bzr branch http://ananelson.com/code/surpass
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;and this will give you a bzr repo named &amp;#8220;surpass&amp;#8221; in whatever directory you typed the command. I&amp;#8217;ll try to set something up with zipped copies of the latest version for those who don&amp;#8217;t want to install Bazaar. Hey! I can automate that with Webby! ;-)&lt;/p&gt;
&lt;p class=&quot;footnote&quot; id=&quot;fn1&quot;&gt;&lt;sup&gt;1&lt;/sup&gt; &lt;a href=&quot;http://lczajkowski.com&quot;&gt;Laura&lt;/a&gt; picked a great &lt;a href=&quot;http://talklikeapirate.com&quot;&gt;day&lt;/a&gt; for OSSBarCamp.&lt;/p&gt;
    </content>
  </entry>
  
  <entry>
    <title>A Comments Engine for Webby</title>
    <link href="/blog/2009/07/a-comments-engine-for-webby/" />
    <id>tag:ananelson.com,2009-07-05:1246806513</id>
    <updated>2009-07-05T16:08:33+01:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;The most common complaint about using &lt;a href=&quot;http://webby.rubyforge.org&quot;&gt;Webby&lt;/a&gt; for a blogging platform is probably &amp;#8220;with static HTML files how can you handle comments?&amp;#8221;. The standard answers are:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Don&amp;#8217;t bother with comments.&lt;/li&gt;
	&lt;li&gt;Use Disqus or some other 3rd party commenting plugin.&lt;/li&gt;
	&lt;li&gt;Build some sort of commenting engine yourself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Today I am going to describe my contribution to this third option. I have extracted the comments engine I use for this blog. For now, you need to install it from source since it&amp;#8217;s not ready to be pushed out as a gem.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bzr branch http://ananelson.com/code/webby_email_comments&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A gem and a nicer name will be forthcoming when I think they are deserved. For now, the code works but it&amp;#8217;s very hard-coded to the way I do things, so if you aren&amp;#8217;t comfortable modifying this to the way you work, you&amp;#8217;re better off waiting until this is a little more polished and configurable.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s basically how this works:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Someone fills in a comment form on one of my blog posts.&lt;/li&gt;
	&lt;li&gt;The form is posted to a PHP script which generates an email which goes to a dedicated email account.&lt;/li&gt;
	&lt;li&gt;Every time I run a &amp;#8220;webby build&amp;#8221;, a script checks for new emails and pulls the contents into a local &lt;a href=&quot;http://datamapper.org&quot;&gt;DataMapper&lt;/a&gt; database. The emails are marked as read once they have been successfully downloaded.&lt;/li&gt;
	&lt;li&gt;When a blog post page is rendered, it checks in this DataMapper database and pulls in any comments for that page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This does mean that you have to render every page freshly in order to include a new comment. There are other possible approaches, such as using JavaScript calls to incorporate comments dynamically. I took this one first since it was the easiest to implement, and it means that comments are available for indexing to search engines.&lt;/p&gt;
&lt;p&gt;If I want to refresh my local cache of comments, I can simply delete the sqlite3 database, mark all the emails as unread, and the next time I build they will all be fetched again. If I receive a spam comment or an unwanted comment, I simply delete the email (or just mark it as read) before running &amp;#8220;webby build&amp;#8221;. I can also interact with comments, delete unwanted ones etc., via DataMapper. If you have legacy comments you want to include, you can just import them into the database.&lt;/p&gt;
&lt;h3&gt;Installing WebbyEmailComments&lt;/h3&gt;
&lt;p&gt;WebbyEmailComments depends on DataMapper and JSON. To get the source, you&amp;#8217;ll need &lt;a href=&quot;http://bazaar-vcs.org&quot;&gt;Bazaar&lt;/a&gt; and to build the gem, you&amp;#8217;ll need &lt;a href=&quot;http://rubyforge.org/projects/codeforpeople/&quot;&gt;bones&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;sudo easy_install bzr &lt;span class=&quot;c&quot;&gt;# Assuming you have Python&amp;#39;s SetupTools installed, or see bazaar-vcs.org for install directions&lt;/span&gt;
sudo gem install bones
sudo gem install json
sudo gem install dm-core dm-more

bzr branch http://ananelson.com/code/webby_email_comments
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;webby_email_comments
sudo rake gem:install
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In your webby project Sitefile, you need to &lt;code&gt;require &quot;webby_email_comments&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Webby uses the &lt;a href=&quot;http://github.com/TwP/loquacious/tree/master&quot;&gt;Loquacious&lt;/a&gt; config engine. This makes it very easy to add additional config settings to Webby, which is done in lib/config.rb in webby_email_comments:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;Loquacious&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configuration_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:webby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Webby author&amp;#39;s email address (your own personal email address)&amp;quot;&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Your dedicated email address for comments&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;The login id to log in to the email server, probably the same as your email address&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;The password to log in to the email server&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;reply_template&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;A message automatically sent to everyone who posts a comment to let them know their comment was received. No message sent if this is nil.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;imap.gmail.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;IMAP server, defaults to gmail&amp;#39;s IMAP&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;993&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;IMAP port&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Whether to use SSL for connecting to IMAP&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;email-comments-db.sqlite3&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Filename for database to store comments.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;php_bin&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;emailbin&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Location to put PHP handler files.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inbox&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Inbox&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:desc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;The name of the Inbox IMAP folder. This folder is checked for new comment messages.&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can set all of these new configuration options in your Sitefile, just as you do your other Webby options, as shown below.&lt;/p&gt;
&lt;p&gt;You can view all the Webby configuration options and their current values by calling:&lt;/p&gt;
&lt;p&gt;&amp;lt;%= Loquacious.help_for(:webby).show :values =&amp;gt; true %&amp;gt;&lt;/p&gt;
&lt;p&gt;It gives you quite a lot of detail, here is a short excerpt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    Location to put PHP handler files.
     - comments.php_bin          =&amp;gt; &quot;emailbin&quot;

    IMAP port
     - comments.port             =&amp;gt; 993
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s a very convenient way of seeing what settings are available, and checking that values are being set correctly.&lt;/p&gt;
&lt;h3&gt;Using WebbyEmailComments&lt;/h3&gt;
&lt;h4&gt;Create a Comment Form&lt;/h4&gt;
&lt;p&gt;There is a comment form helper built in. In your layout template, just call &amp;lt;%= comment_form %&amp;gt; whereever you want the comment form to appear. I actually call the comment form like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    &amp;lt;%= comments_for_page %&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;% if @page.dirty -%&amp;gt;
    &amp;lt;%= comment_form %&amp;gt;
    &amp;lt;% end -%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;comments_for_page is the helper which displays the actual comments.&lt;/p&gt;
&lt;p&gt;This means that in order to enable comments on a blog post, I must add &lt;code&gt;dirty: true&lt;/code&gt; to the Webby metadata for that page. This is a good idea anyway since it will mean that new comments will get added to that page every time you build your site.&lt;/p&gt;
&lt;p&gt;There are a few spam prevention attempts built in to WebbyEmailComments (I have no idea how effective they are as I haven&amp;#8217;t studied my logs yet). One of them is to have a secret code built in to the form and the receiving PHP script. In order for this to work, the PHP script and the comment forms must be generated at the same time so they share the same key.&lt;/p&gt;
&lt;p&gt;Hence, I recommend you follow the same procedure:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;To enable comments on a blog post, set dirty=true.&lt;/li&gt;
	&lt;li&gt;In your blog layout, show the comment form only if dirty=true.&lt;/li&gt;
	&lt;li&gt;Ensure that dirty=true is kept in the PHP form (it&amp;#8217;s there by default).&lt;/li&gt;
	&lt;li&gt;When you are ready to close comments for a post, simply remove dirty=true from the metadata.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, in your CSS you should have something like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nc&quot;&gt;.emphasise&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;to hide the math question which is just there to trick bots.&lt;/p&gt;
&lt;h4&gt;PHP Processing&lt;/h4&gt;
&lt;p&gt;There is a &lt;code&gt;template/&lt;/code&gt; directory in webby_email_comments which contains the PHP script to process completed forms, along with a copy of XPertMailer, an open source PHP library to create emails. I have minimal PHP experience so if anyone wants to suggest a better library, I&amp;#8217;m happy to add that in, although XPertMailer seems to work very well and is very easy to use.&lt;/p&gt;
&lt;p&gt;This PHP script is dynamic, it gets processed via ERb each time to incorporate the secret key and to fill in user preferences like what your email address for comments is.&lt;/p&gt;
&lt;p&gt;If you run the webby_email_comments executable (which should be available after you install the gem), from the root directory of your webby installation, it will create a directory called &amp;#8220;emailbin&amp;#8221; in your webby content directory and install your PHP handling and the XPertMailer library. You can change the name of this directory in the config.&lt;/p&gt;
&lt;p&gt;You can then edit the PHP code yourself if you want to make any changes, and you should just upload this directory along with the rest of your site, taking care to check that the permissions on the PHP files are correct (e.g. not world-writable).&lt;/p&gt;
&lt;h4&gt;Moderating Comments&lt;/h4&gt;
&lt;p&gt;The easiest way to get started is to create a gmail account like &amp;#8220;blog-comments-ananelson@gmail.com&amp;#8221; and specify this in your Sitefile as follows:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;webby_email_comments&amp;quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# must require this before setting configs&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;SITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;ana@ananelson.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;SITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my.comments.email.address@gmail.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;SITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reply_template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Hey, thanks for leaving a comment. :-) ...&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;SITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;my.comments.email.address@gmail.com&amp;#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;SITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;passw0rd&amp;#39;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# yeah, right&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;The SITE.comments.email is your dedicated email for comments, SITE.email is your real email address and the reply_template is an acknowledgement message which gets sent back to people who leave a comment. (No acknowledgement is sent if reply_template is nil.) A copy of each comment gets sent to your personal email as well as the comments email so you know when you have received a comment. Just delete emails of unwanted comments in your comments.email, or mark them as spam if you want to try to train gmail (or whatever spam filter you use) to recognize spam.&lt;/p&gt;
&lt;p&gt;I have a rake task to check for new comments.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;initializing comments database...&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;WebbyEmailComments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db_init&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;fetch latest comments...&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WebbyEmailComments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process_inbox&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;unable to connect to comments email, skipping...&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;process_inbox does all the work. It returns false if there has been a SocketError, such as when you don&amp;#8217;t have internet access, and true if all is well.&lt;/p&gt;
&lt;p&gt;By specifying &lt;code&gt;task :build =&amp;gt; :comments&lt;/code&gt;, you can ensure that the latest comments are fetched every time you rebuild. If you want to check comments more methodically, then don&amp;#8217;t add this dependency and run webby comments manually.&lt;/p&gt;
&lt;p&gt;Currently only IMAP is supported. The default host, port and ssl settings will work if you use gmail. Otherwise set the variables in your Sitefile to the appropriate values.&lt;/p&gt;
&lt;p&gt;The database defaults to email-commends-db.sqlite3 in your webby root directory, you can change this (such as to put it in a comments/ directory or somewhere else out of the way) by specifying SITE.comments.db in your Sitefile.&lt;/p&gt;
&lt;h4&gt;Displaying Comments&lt;/h4&gt;
&lt;p&gt;As discussed above, place &amp;lt;%= comments_for_page %&amp;gt; in your template wherever you wish comments to be displayed.&lt;/p&gt;
&lt;h4&gt;Getting Help, Asking Questions&lt;/h4&gt;
&lt;p&gt;If you have questions, feel free to post a comment here or on the &lt;a href=&quot;http://groups.google.com/group/webby-forum&quot;&gt;Webby mailing list&lt;/a&gt;. Once again, this is early days and for now this is intended for people who are pretty comfortable with using Webby already.&lt;/p&gt;
    </content>
  </entry>
  
  <entry>
    <title>Using Bazaar With Capistrano on Rails Machine</title>
    <link href="/blog/2009/03/using-bazaar-with-capistrano-on-railsmachine/" />
    <id>tag:ananelson.com,2009-03-23:1237845937</id>
    <updated>2009-03-23T22:05:37+00:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;ul&gt;
	&lt;li&gt;How to convert a Subversion repository to &lt;a href=&quot;http://bazaar-vcs.org/&quot;&gt;Bazaar&lt;/a&gt; using Tailor.&lt;/li&gt;
	&lt;li&gt;Modifying your deploy.rb to use Bazaar for &lt;a href=&quot;http://www.capify.org/&quot;&gt;Capistrano&lt;/a&gt; deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;I have been using the standard &lt;a href=&quot;http://railsmachine.com/&quot;&gt;Rails Machine&lt;/a&gt; Capistrano setup (as described in the original Five-Minute Rails Application Deployment page). The only alteration I have made to this is the use of &lt;a href=&quot;http://svk.bestpractical.com/view/HomePage&quot;&gt;SVK&lt;/a&gt; as a local wrapper for Subversion. (I started writing this blog post some months ago, and the Rails Machine instructions now suggest using Git rather than Subversion for version control.)&lt;/p&gt;
&lt;p&gt;My setup has served me well overall, but apart from not getting to use Bazaar (which I really enjoy), I have 2 gripes with it:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;If I am starting a new project which I plan to deploy on Rails Machine, I have to either start over with a new repository when I am ready to deploy (losing my repository history up to that point), or I have to manually upload and import my repository contents after creating a blank repository as per the Five Minute guide.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;If I remove a project from Rails Machine, I have the same problem in reverse. I need to manually export and recreate the repository elsewhere if I want to keep it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These aren&amp;#8217;t everyday issues, I probably have to deal with this sort of situation only a few times every year. But, when I was starting a new project recently I began working with Bazaar and, when the time came to deploy, I decided to try to find a way around switching to Subversion. After that, I became anxious to convert all my deployed projects to the same setup.&lt;/p&gt;
&lt;p&gt;So, my new approach is:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;A local Bazaar repository for development, pushed to some other remote location for backup and remote access.&lt;/li&gt;
	&lt;li&gt;A Capistrano deployment recipe based on the Copy strategy. The Copy strategy is very straightforward, and it avoids the need to install Bazaar (and all the extra bits needed for SSH) on your deployment server.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Get Out of Subversion&lt;/h2&gt;
&lt;p&gt;The first step in the process is to convert my existing Subversion repositories to Bazaar. You can, of course, choose to ignore your version history and just import the current version of your project into a blank Bazaar repository. You lose your version history this way, but for some projects the simplicity of this approach might outweigh the posterity value of having the full code history.&lt;/p&gt;
&lt;p&gt;There is, however, a tool which will let you export your whole repository history from Subversion into Bazaar. There are, in fact, &lt;a href=&quot;http://bazaar-vcs.org/BzrMigration&quot;&gt;several such tools&lt;/a&gt;, but I am going to use &lt;a href=&quot;http://wiki.darcs.net/DarcsWiki/Tailor&quot;&gt;Tailor&lt;/a&gt; which supports several version control systems (including Git).&lt;/p&gt;
&lt;p&gt;Unless you happen to have &lt;a href=&quot;http://www.darcs.net/&quot;&gt;darcs&lt;/a&gt; already installed, I don&amp;#8217;t suggest you try to get the latest development version of Tailor. Much easier to just &lt;a href=&quot;http://darcs.arstecnica.it/&quot;&gt;download a released version&lt;/a&gt; of Tailor. If you do decide to install darcs, read &lt;a href=&quot;http://wiki.darcs.net/DarcsWiki/CategoryBinaries#head-2193376ebf5900d9c5848fa1c1410ebbd2ad3c74&quot;&gt;this page&lt;/a&gt; first, especially the part about how the GHC can take 3 hours to compile.&lt;/p&gt;
&lt;h2&gt;Using Tailor&lt;/h2&gt;
&lt;h3&gt;Preliminaries&lt;/h3&gt;
&lt;p&gt;Tailor needs a directory in which it can do its dirty work. To ensure we always start with a clean setup, I prefer to write a script to prepare the environment:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;mkdir ~/tailor-tmp
rm ~/tailor-tmp/tailor.state

rm -rf ~/tailor-tmp/work/
mkdir ~/tailor-tmp/work/
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This can and should be run prior to each time you actually run tailor. (The workflow I am describing here is for a once-off use of Tailor to convert a repository. Tailor can also be used to keep 2 repositories in sync, in which case of course you would not delete the working directory and state file.)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3&gt;Creating a Config File&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;http://progetti.arstecnica.it/tailor/browser/README.rst&quot;&gt;tailor documentation&lt;/a&gt; describes the options available to you in the config file, but it&amp;#8217;s not written in a way which makes it easy to write a file from scratch. I eventually figured out a recipe which worked for me by getting Tailor to create a blank config file and then tweaking it.&lt;/p&gt;
&lt;p&gt;You can get Tailor to generate a blank config file as easily as:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;tailor --verbose &amp;gt; blank-tailor-config.tailor
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;However, moving into &lt;code&gt;~/tailor-tmp&lt;/code&gt; first and passing a few simple tailor switches makes the config file much more complete:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/tailor-tmp
tailor --verbose -s svn -t bzr -R /path/to/repo &amp;gt; blank-tailor-config.tailor
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;This gives you:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;[DEFAULT]&lt;br /&gt;
verbose = True&lt;/p&gt;
&lt;p&gt;[project]&lt;br /&gt;
target = bzr:target&lt;br /&gt;
start-revision = INITIAL&lt;br /&gt;
root-directory = /Users/ana/tailor-temp&lt;br /&gt;
state-file = tailor.state&lt;br /&gt;
source = svn:source&lt;br /&gt;
subdir = .&lt;/p&gt;
&lt;p&gt;[bzr:target]&lt;/p&gt;
&lt;p&gt;[svn:source]&lt;br /&gt;
repository = /path/to/repo&lt;br /&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;/notextile&gt;&lt;/p&gt;
&lt;p&gt;You can read more about all these options in the &lt;a href=&quot;http://progetti.arstecnica.it/tailor/browser/README.rst#projects&quot;&gt;projects section&lt;/a&gt; of the Tailor documentation.&lt;/p&gt;
&lt;p&gt;The only item we need to change in [DEFAULT] or [project] is to set&lt;/p&gt;
&lt;p&gt;&lt;code&gt;subdir = work&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The subdir is where Tailor does its magic, and it&amp;#8217;s crucial to start with an empty subdirectory each time you run Tailor, hence this directory (along with the tailor.state file) is deleted and recreated in the &lt;code&gt;prepare.sh&lt;/code&gt; script.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;start-revision = INITIAL&lt;/code&gt; is not the same as &lt;code&gt;start-revision = 1&lt;/code&gt;. &lt;code&gt;start-revision = INITIAL&lt;/code&gt; uses &lt;code&gt;--stop-on-copy&lt;/code&gt; to help calculate when the current repository was created and, hence, when it should start its export. If you are working off a repository which was forked midway through its life, but you nonetheless want the entire history, then set &lt;code&gt;start-revision = 2&lt;/code&gt; (or whenever the trunk/ directory was added to the repository, probably revision 2 if Capistrano created your repository for you). You might also decide to look at your log and skip over the first few irrelevant commits.&lt;/p&gt;
&lt;p&gt;Next, we have to tell Tailor where our source repository is. The source repository is the remote Subversion repository on your Rails Machine server. You should replace &lt;code&gt;/path/to/repo&lt;/code&gt; with the actual location of your remote repository, and then to indicate that we only want the &lt;code&gt;trunk&lt;/code&gt;, we add &lt;code&gt;module = trunk&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;[svn:source]
repository = svn+ssh://deploy@subdomain.railsmachina.com/var/www/apps/appname/repos
module = trunk
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you need &lt;code&gt;branches&lt;/code&gt; or &lt;code&gt;tags&lt;/code&gt;, then you can run Tailor again and export these separately, or if you want to keep a Subversion-style setup (i.e. trunk/ branches/ tags/) you can set &lt;code&gt;module = /&lt;/code&gt;. Or, take a look at the &lt;code&gt;svn-tags&lt;/code&gt; and &lt;code&gt;svn-branches&lt;/code&gt; options in the Tailor docs.&lt;/p&gt;
&lt;p&gt;We don&amp;#8217;t need to specify a target repository, since Tailor will automatically initialize a blank Bazaar repository when it runs.&lt;/p&gt;
&lt;p&gt;Once the config file is all ready, actually running tailor is a one-liner:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;tailor --configfile config.tailor
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you have &lt;a href=&quot;https://support.railsmachine.com/index.php?pg=kb.page&amp;amp;id=33&quot;&gt;set up SSH keys&lt;/a&gt;, then you can sit back and relax, watching the verbose output. If you don&amp;#8217;t have SSH keys, you will need to enter your password several times during the process, at least once for every revision which is exported.&lt;/p&gt;
&lt;p&gt;If all goes smoothly, then the &lt;code&gt;~/tailor-tmp/work&lt;/code&gt; directory will be a bazaar repository. You can copy this cleanly by branching it:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;bzr branch ~/tailor-tmp/work ~/bzr-repositories/projectname
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should &lt;code&gt;bzr push&lt;/code&gt; this to a remote location for safe-keeping.&lt;/p&gt;
&lt;p&gt;If you have multiple repositories to export, all you need to change is the source repository location and you can run the script again.&lt;/p&gt;
&lt;h3&gt;Troubleshooting Tailor&lt;/h3&gt;
&lt;p&gt;If you get strange error messages, make sure you have deleted all temporary files. If in doubt, backup your config file and remove the entire ~/tailor-tmp/ directory and start over. (Note: there is no need to have the config file stored in ~/tailor-tmp/, it can be anywhere.)&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s possible that Bazaar will choke on importing some portion of your Subversion output. I have stumbled across bizarre combinations of adding and deleting directories which confuse Bazaar. Hopefully these minor bugs have all been fixed in the latest version. If this happens to you, do file a bug. If you can&amp;#8217;t get around it, then best option I can suggest is to start the import at a higher revision. If Bazaar doesn&amp;#8217;t like revision 11, then set &lt;code&gt;start-revision = 12&lt;/code&gt; in your config file and live with the loss.&lt;/p&gt;
&lt;h2&gt;Using Capistrano with Bazaar&lt;/h2&gt;
&lt;p&gt;There are just a few settings to change in deploy.rb to use your new Bazaar repository for deployment.&lt;/p&gt;
&lt;p&gt;Change the &lt;code&gt;:repository&lt;/code&gt; switch from something like this:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;svn+ssh://&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deploy_to&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/repos/trunk&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;to something like this:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deploy_via&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:copy&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:copy_strategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:export&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:copy_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;And you&amp;#8217;ll need to specify the form of version control being used. Since Subversion is the default, there may be a commented-out line of code in your deploy.rb like this:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;#set :scm,&amp;quot;subversion&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Uncomment this line and use the string &amp;#8220;bzr&amp;#8221; to indicate Bazaar:&lt;br /&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:scm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;bzr&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;There is one final tweak necessary, and this is a slight alteration to the railsmachine gem itself.&lt;/p&gt;
&lt;p&gt;You&amp;#8217;ll need to add &lt;code&gt;bzr&lt;/code&gt; to the list of &lt;a href=&quot;http://github.com/railsmachine/railsmachine/blob/0a8dfb04a6e48e89b3e4bec5efc8d1f4fe2e6fcc/lib/railsmachine/recipes.rb#LID59&quot;&gt;allowed version control systems&lt;/a&gt;, and you&amp;#8217;ll need to create an empty file named bzr.rb in the &lt;a href=&quot;http://github.com/railsmachine/railsmachine/tree/0a8dfb04a6e48e89b3e4bec5efc8d1f4fe2e6fcc/lib/railsmachine/recipes/scm&quot;&gt;lib/railsmachiine/recipes/scm/ directory&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With these changes made, you should now be able to &lt;code&gt;cap deploy&lt;/code&gt; from your new Bazaar repository.&lt;/p&gt;
    </content>
  </entry>
  
  <entry>
    <title>Surpass Talk to Python Ireland</title>
    <link href="/blog/2009/03/surpass-talk-to-python-ireland/" />
    <id>tag:ananelson.com,2009-03-23:1237838737</id>
    <updated>2009-03-23T20:05:37+00:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;I gave a talk about &lt;a href=&quot;http://surpass.rubyforge.org&quot;&gt;Surpass&lt;/a&gt; and &lt;a href=&quot;http://pypi.python.org/pypi/xlwt&quot;&gt;XLWT&lt;/a&gt; recently to &lt;a href=&quot;http://python.ie&quot;&gt;Python Ireland&lt;/a&gt;, as part of an evening themed as joint Ruby/Python talks. Surpass is a native Ruby library for creating Excel spreadsheets, which I will be writing a lot more about in the near future. It's a port to Ruby of the Python library XLWT. &lt;a href=&quot;http://kaykays.com/personal/&quot;&gt;Vicky Lee&lt;/a&gt; did a fantastic job of recording and editing my talk, and it's up on Vimeo if you'd like to hear me talk about why you might want to create Excel spreadsheets, and how to do it with Surpass (or XLWT).&lt;/p&gt;

&lt;p&gt;You can also &lt;a href=&quot;spreadsheets-for-ruby-and-python.pdf&quot;&gt;download the slides&lt;/a&gt; in PDF format.&lt;/p&gt;

&lt;object width=&quot;640&quot; height=&quot;360&quot;&gt;&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot; /&gt;&lt;param name=&quot;movie&quot; value=&quot;http://vimeo.com/moogaloop.swf?clip_id=3800419&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1&amp;amp;group_id=&quot; /&gt;&lt;embed src=&quot;http://vimeo.com/moogaloop.swf?clip_id=3800419&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1&amp;amp;group_id=&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; allowscriptaccess=&quot;always&quot; width=&quot;640&quot; height=&quot;360&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;a href=&quot;http://vimeo.com/groups/4190/videos/3800419&quot;&gt;Python/Ruby Ireland Talks - March 2009 (1 of 3)&lt;/a&gt; from &lt;a href=&quot;http://vimeo.com/whykay&quot;&gt;whykay&lt;/a&gt; on &lt;a href=&quot;http://vimeo.com&quot;&gt;Vimeo&lt;/a&gt;.

    </content>
  </entry>
  
  <entry>
    <title>Inspect an Array of Variables</title>
    <link href="/blog/2008/10/inspect-an-array-of-variables/" />
    <id>tag:ananelson.com,2008-10-23:1224788737</id>
    <updated>2008-10-23T20:05:37+01:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;Most likely, everyone&amp;#8217;s been doing this forever but I just figured it out:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;want&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;want&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;see&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:inspect&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;lots&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;once&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;of variables at once&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Generally, I prefer this:&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;see&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lots&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inspect&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;instead of this:&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;want&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;see&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lots&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;once&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;Generally, I prefer this:
[1, &amp;quot;want&amp;quot;, 2, :inspect, 1..100, &amp;quot;of&amp;quot;, &amp;quot;variables&amp;quot;, &amp;quot;at&amp;quot;, &amp;quot;once&amp;quot;]

instead of this:
1
want
2
inspect
1..100
of
variables
at
once
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s a handy shortcut which I discovered while porting a Python project to Ruby recently, which involved lots of manual debugging.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;works&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_python&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;too&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;works&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_python&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;too&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
    </content>
  </entry>
  
  <entry>
    <title>A Rails Application Factory</title>
    <link href="/blog/2008/08/a-rails-application-factory/" />
    <id>tag:ananelson.com,2008-08-28:1219954459</id>
    <updated>2008-08-28T21:14:19+01:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;This is a post about creating fresh Rails applications quickly and maintaining them easily over time. The approach I take here could easily be applied outside of Rails to any software project.&lt;/p&gt;
&lt;h3&gt;My Philosophy of Friction&lt;/h3&gt;
&lt;p&gt;I like to think about productivity in terms of &amp;#8220;friction&amp;#8221;. My inner economist wants me to call this &amp;#8220;marginal cost&amp;#8221; but my inner consultant thinks &amp;#8220;friction&amp;#8221; will sell better. The principle I try to organize by is that things which you want yourself to do on a regular basis: exercise, eat well, churn out a shiny prototype in next to no time; should be quick and painless. You should try to reduce the friction between you and your desired outcomes. So, make it easy to eat well by stocking the kitchen with plenty of healthy, easy-to-prepare food. If you never go to the gym because there&amp;#8217;s never any parking, or you have to make a tricky right turn to get into the car park (I live in Ireland, remember, where right turns are the tricky ones), this is friction and the more friction there is, the less likely you are to do something. Join a different gym, or make yourself consciously aware of the friction and make the decision to go to the gym anyway. Friction is nefarious since it works on our subconscious, sometimes just being aware of it can remove its influence, but staying aware of it is difficult.&lt;/p&gt;
&lt;p&gt;Reducing friction for good things, and increasing friction for bad things (put those cookies on the TOP shelf), can be a simple and effective guideline for orienting your practices towards your goals. (But, please do remember that marginal thinking does not show you the entire picture. Somewhere you need to think about the sum of what you are doing, not just marginal increases or decreases. Friction is very useful as a short-term heuristic, though, and it&amp;#8217;s also usually the right tool for predicting behaviour in other people, especially users of software. Check out Why We Buy by Paco Underhill, one of my all-time favourite books for its content and basically all about friction.)&lt;/p&gt;
&lt;p&gt;So, this week I decided to reduce a few forms of friction in my programming life. To begin with, I wanted to improve my strategy for creating a fresh Rails application.&lt;/p&gt;
&lt;h3&gt;Creating A Fresh App&lt;/h3&gt;
&lt;p&gt;Of course, actually building a generic Rails application is very straightforward, you simply type:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;rails example
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and you will have a new directory called &lt;code&gt;example&lt;/code&gt; which contains a skeleton Rails application. Very quickly, though, a skeleton Rails application isn&amp;#8217;t going to be enough. Most developers will have a collection of plugins, rake tasks and other goodies which they want to install on every application. I head straight for &lt;code&gt;rspec&lt;/code&gt; and &lt;code&gt;active_scaffold&lt;/code&gt;. Also, I don&amp;#8217;t have any use for &lt;code&gt;public/index.html&lt;/code&gt; or the &lt;code&gt;test/&lt;/code&gt; directory so I will always delete these. The details don&amp;#8217;t really matter, the point is that there is a series of repetitive tasks which we do whenever we set out to create a new Rails application.&lt;/p&gt;
&lt;p&gt;My original approach to automating this was to write a shell script which went through the various steps for me:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Usage: bash setup_rails projectname&lt;/span&gt;
rails &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec
ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails
ruby script/generate rspec
ruby script/plugin install http://svn.caldersphere.net/svn/main/plugins/rspec_autotest
svn &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;http://activescaffold.googlecode.com/svn/trunk vendor/plugins/active_scaffold
svn &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;http://activescaffoldexport.googlecode.com/svn/trunk vendor/plugins/activescaffoldexport
svn &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;http://dev.agent.ie/svn/rails/generators/ajax_rspec_resource/ lib/generators/ajax_rspec_resource
ruby script/plugin install exception_notification

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;About to drop and recreate development and test databases. Next password prompt is for mysql user password.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;drop database if exists $1_development;&amp;quot;&lt;/span&gt; | mysql -u root -p
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;drop database if exists $1_test;&amp;quot;&lt;/span&gt; | mysql -u root -p
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;create database $1_development;&amp;quot;&lt;/span&gt; | mysql -u root -p
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;create database $1_test;&amp;quot;&lt;/span&gt; | mysql -u root -p
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wow, mysql and svn, that takes me back. ;-)&lt;/p&gt;
&lt;p&gt;With this script, I could create a new Rails application in a few seconds and my favourite plugins would be installed and configured, databases would be set up and a custom generator installed and ready to go.&lt;/p&gt;
&lt;p&gt;This was definitely better than nothing, but it had some problems. I would tend to have a spurt of starting a few new projects, and then there would be a lull of a few months where I was working on existing projects. By the time I went back to use this script again, things would have changed. Plugins would have moved or I would be using different ones. Or, I would forget I had written the script and find myself halfway through setting up an application manually before I remembered that, yes, I invented an easier way to do this once upon a time. But, the crucial flaw in the &lt;code&gt;setup_rails.sh&lt;/code&gt; concept is that it does nothing to help you automate the maintenance of your Rails applications. I had automated the birth process, but not the much more onerous ongoing maintenance which is necessary for any piece of software with external dependencies.&lt;/p&gt;
&lt;p&gt;So, finally, this morning I worked out a strategy which is going to solve all of these problems. There&amp;#8217;s no script involved, just some really easy branching and merging using, in my case, the &lt;a href=&quot;http://bazaar-vcs.org&quot;&gt;Bazaar version control system&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The strategy is:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;create a skeleton Rails application configured just the way I like it named &lt;code&gt;template-rails&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;each time I want to create a new Rails application, I &lt;code&gt;bzr branch template-rails my_new_app&lt;/code&gt;. This creates a copy of the contents of template-rails in a new directory called my_new_app, and the my_new_app branch &amp;#8220;remembers&amp;#8221; that it originated from template-rails.&lt;/li&gt;
	&lt;li&gt;each time I upgrade a plugin, tweak a rake task, write a new spec helper or anything else that I want to share amongst more than one application, I make this change to &lt;code&gt;template-rails&lt;/code&gt; and then bring the changes into each application by just typing &lt;code&gt;bzr merge&lt;/code&gt; and, later, committing the changes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, this covers both creating and maintaining applications in one easy step. Because I interact with version control on a daily basis, I&amp;#8217;m not going to forget what my setup is, whereas I might easily forget about a six-month-old shell script tucked away in some bin/ directory.&lt;/p&gt;
&lt;h3&gt;Bazaar Tricks&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;m sure you could do this with any version control system that has good (low-friction!) support for branching and merging. I happen to be partial to bazaar because, coming from subversion, it took me 5 minutes to learn the basics but I&amp;#8217;m still discovering wonderful little features that make my life so much easier. Just this morning, for example, I learned that if you &lt;code&gt;bzr rm --new --keep&lt;/code&gt;, bazaar will remove whichever files you have just accidentally added to your repository without deleting them, they just go back to being &amp;#8220;unknown&amp;#8221; files. This is GREAT when you add several directories without adding, say, &amp;#8220;.git&amp;#8221; to your .bzrignore file. You can just undo the add, tell .bzr to ignore the pattern, then safely add just the files you want. When I use a plugin from &lt;a href=&quot;http://github.com&quot;&gt;github&lt;/a&gt; I &lt;code&gt;git clone&lt;/code&gt; it and leave the git branch information intact. It doesn&amp;#8217;t conflict with bzr and I can easily pull down changes.&lt;/p&gt;
&lt;p&gt;A trick which is particularly useful for working with a template like I have been describing is described in &lt;a href=&quot;http://doc.bazaar-vcs.org/latest/en/user-guide/index.html#pseudo-merging&quot;&gt;Section 7.2.3 of the Bazaar user guide&lt;/a&gt;. While I would probably go into template-rails/ to pull down the latest version of a plugin, if I am editing something I have written myself like a rake task or a spec helper, chances are I&amp;#8217;m going to be working on this and testing this in a live application. But, once I have upgraded or fixed the code, I probably want to bring this change back into my template so it can be shared with my other apps and any apps I create by branching this template in the future. Here is a nice little workflow assuming I start out in my example/ application directory:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ../template-rails
bzr merge --uncommitted example/
bzr diff
bzr commit . -m &lt;span class=&quot;s2&quot;&gt;&amp;quot;Refactor matchers to derive description automatically from class name.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ../example
bzr merge
bzr commit . -m &lt;span class=&quot;s2&quot;&gt;&amp;quot;Merged changes from template.&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;merge --uncommitted example/&lt;/code&gt; is just a fantastic little command which gets whatever changes you have made in the example/ branch but not yet committed (this does not know about &amp;#8220;unknown&amp;#8221; files, so if you create a new file you have to add it to the repository for it to show up here). So, you can happily develop and test in any of your branches and pulling the changes back to the template is a couple of quick commands away.&lt;/p&gt;
&lt;h3&gt;Sharing Code&lt;/h3&gt;
&lt;p&gt;A side effect of adopting a system like this is, because you have reduced the pain (or friction, or marginal cost) associated with sharing code between your applications, you are more likely to share code. You are more likely to write test helpers, custom rake tasks and your own plugins. Sharing code between your applications is a great way to improve it and the lessons you learn from testing your plugin on its 2nd Rails application will improve its performance and stability in the Rails application you originally developed it for. You are more likely to keep all your Rails applications running on an up-to-date version of Rails too.&lt;/p&gt;
&lt;p&gt;I have mentioned Rails and some Rails-specific terminology quite a lot, but of course the idea of setting out a standard structure in a template and branching from this could be applied to very many situations. In fact, because Rails has a pretty good directory structure by default, I&amp;#8217;m guessing I will feel the benefit of this approach much more in non-Rails project. For example, I am doing a lot of data analysis work using just DataMapper and a console, no web framework of any kind required, and I have started to work with a certain self-imposed project structure but now I am going to set up a template so that I don&amp;#8217;t succumb to the temptation to re-invent this structure on every project.&lt;/p&gt;
&lt;p&gt;One of the reasons I am pretty excited about sharing code between applications easily is that I have been developing some very fun tools over the past few weeks for rapid model prototyping in Rails using &lt;code&gt;rspec&lt;/code&gt; and &lt;code&gt;railroad&lt;/code&gt;. Stay tuned.&lt;/p&gt;
    </content>
  </entry>
  
  <entry>
    <title>JRuby Wrapping For ANTLR</title>
    <link href="/blog/2008/06/jruby-wrapping-for-antlr/" />
    <id>tag:ananelson.com,2008-06-27:1214588035</id>
    <updated>2008-06-27T18:33:55+01:00</updated>
    <content type="html">
        &lt;p&gt;Please visit the website for source code downloads and syntax highlighting.&lt;/p&gt;&lt;p&gt;I use &lt;a href=&quot;http://antlr.org/&quot;&gt;ANTLR&lt;/a&gt;, a fantastic parser generator, for various parsing tasks and experimenting with DSLs&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. ANTLR is one of the tools that makes it possible to &amp;#8220;write code that writes code&amp;#8221;. Previously I wrote about &lt;a href=&quot;http://ananelson.com/said/on/2008/02/11/redirecting-java-output-streams-in-jruby-and-antlr-unit-testing/&quot;&gt;capturing Java&amp;#8217;s output and error streams&lt;/a&gt; in JRuby, and I used this trick to write a wrapping for ANTLR which makes it very easy to unit test grammars as well as to integrate ANTLR parsers into a Ruby class. I call it AntlrVelvet.&lt;/p&gt;
&lt;p&gt;AntlrVelvet is just a Ruby module, so you need to require the file and then include the module in a class:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;antlr_velvet&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Expr&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AntlrVelvet&lt;/span&gt;  
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And&amp;#8230; that&amp;#8217;s it. We now have a parser! By default, AntlrVelvet assumes you have given your class the same name as you gave your parser, in this case &lt;code&gt;Expr&lt;/code&gt;. (You can easily override this.) &lt;code&gt;ExprLexer.class&lt;/code&gt; and &lt;code&gt;ExprParser.class&lt;/code&gt; need to be on the Java &lt;code&gt;CLASSPATH&lt;/code&gt; somewhere, but if they&amp;#8217;re in the same directory as &lt;code&gt;antlr_velvet.rb&lt;/code&gt; this will be taken care of automatically.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s take a quick look at &lt;code&gt;Expr.g&lt;/code&gt;, the ANTLR grammar file, and see what super-powers we have given our Ruby class with these 4 lines of boilerplate.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;// http://www.antlr.org/wiki/display/ANTLR3/Expression+evaluator&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// [The &amp;quot;BSD licence&amp;quot;] Copyright (c) 2005-2008 Terence Parr All rights reserved.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;grammar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;@header&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;@members&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;sr&quot;&gt;/** Map variable name to Integer object holding value */&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;HashMap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NEWLINE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NEWLINE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NEWLINE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;multExpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;multExpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;multExpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;multExpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$INT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;Integer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v!&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;undefined variable &amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;(&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;)&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;z&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;Z&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;9&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;NEWLINE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;\r&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;WS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;\t&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://www.antlr.org/wiki/display/ANTLR3/Expression+evaluator&quot;&gt;&lt;code&gt;Expr.g&lt;/code&gt;&lt;/a&gt; is taken directly from the ANTLR wiki, it is an expression evaluator. We can assign integer values to variables and perform simple arithmetic operations.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s start feeding in some strings. If you have &lt;a href=&quot;http://jruby.codehaus.org/&quot;&gt;JRuby&lt;/a&gt; and &lt;a href=&quot;http://antlr.org/&quot;&gt;ANTLR&lt;/a&gt; installed, you can download &lt;code&gt;antlr_velvet_demo.rb&lt;/code&gt; etc. and play along.&lt;/p&gt;
&lt;p&gt;AntlrVelvet has convenience methods for parsing files as well as strings. We&amp;#8217;ll just parse strings. To initialize a new string parser, you do:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;parser&lt;/code&gt; is now all ready to parse, but the parsing hasn&amp;#8217;t happened yet. Because AntlrVelvet is designed for unit testing, it doesn&amp;#8217;t make any assumptions about how you want to parse. If you call &lt;code&gt;parser.prog&lt;/code&gt;, it will parse according to the &lt;code&gt;Expr&lt;/code&gt; grammar&amp;#8217;s main &lt;code&gt;prog&lt;/code&gt; rule. You might only want to test a part of your grammar, however, such as an &lt;code&gt;atom&lt;/code&gt; or an &lt;code&gt;expr&lt;/code&gt;. You can call any rule defined in your grammar, e.g. &lt;code&gt;parser.atom&lt;/code&gt;, and the string you passed at initialization will be evaluated against that rule.&lt;/p&gt;
&lt;p&gt;So, let&amp;#8217;s try this now:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prog&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;we get:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;line 0:-1 missing NEWLINE at &amp;#39;&amp;lt;EOF&amp;gt;&amp;#39;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Oops. Okay, we tried comparing &lt;code&gt;&quot;1 + 1&quot;&lt;/code&gt; to the &lt;code&gt;prog&lt;/code&gt; rule and we got an error, because the &lt;code&gt;prog&lt;/code&gt; rule expects to process one or more &lt;code&gt;stat&lt;/code&gt; rules, each of which are newline-terminated. If we add a newline at the end:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prog&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;we get:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;nil
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;nil&lt;/code&gt;? Honestly, how hard is &lt;code&gt;1 + 1 = 2&lt;/code&gt;? Okay, looking back at &lt;code&gt;Expr.g&lt;/code&gt; I see that &lt;code&gt;prog&lt;/code&gt; doesn&amp;#8217;t actually return a value. Let&amp;#8217;s try an &lt;code&gt;expr&lt;/code&gt; since according to the grammar that should return an &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;2
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;2! Excellent. After all this we can add 1 and 1.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#8217;s take another look at &lt;code&gt;stat&lt;/code&gt;, since that really does seem to be the main action. We don&amp;#8217;t return anything, but we do write to &lt;code&gt;System.out.println()&lt;/code&gt;. Remember AntlrVelvet captures Java&amp;#8217;s output and error streams so it can look for parsing errors and raise an exception if it sees one. If you want to get at the contents of the output stream, you do this via the &lt;code&gt;output&lt;/code&gt; method.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stat&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inspect&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;[&amp;quot;2&amp;quot;]
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, we have an array with a 2 in it. This is because &lt;code&gt;stat&lt;/code&gt; prints out &lt;code&gt;expr.value&lt;/code&gt; every time it gets called. Now, let&amp;#8217;s try calling &lt;code&gt;prog&lt;/code&gt; again with some additional input.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;1 + 1&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;x = 1&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;y = 2&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;3*(x+y)&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prog&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inspect&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;[&amp;quot;2&amp;quot;, &amp;quot;9&amp;quot;]
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first &amp;#8220;2&amp;#8221; comes from 1 + 1. The lines x = 1 and y = 2 are of the form &lt;code&gt;ID = expr NEWLINE&lt;/code&gt;, they are variable assignments rather than &lt;code&gt;expr&lt;/code&gt; evaluations, so nothing is added to the output buffer, instead the parser executes the &lt;code&gt;memory.put ...&lt;/code&gt; action. The final line is an &lt;code&gt;expr&lt;/code&gt; which evaluates to 9 and so this is added to the output buffer. Note the extra newline at the end of our input, if this wasn&amp;#8217;t there we would end up with an &lt;code&gt;EOF&lt;/code&gt; error. (Of course you can easily write a grammar which doesn&amp;#8217;t need a newline at the end if you want to.)&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s do one more example.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 2 * (3 + 4 * (5 + 6 * (7 + 8 * 9)))&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expr&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;3839
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&amp;#8217;s some nice mutual recursion in &lt;code&gt;Expr.g&lt;/code&gt; that lets it handle nested parentheses like this, go check it out if you haven&amp;#8217;t seen it before.&lt;/p&gt;
&lt;p&gt;Now, &lt;code&gt;Expr.g&lt;/code&gt; doesn&amp;#8217;t do anything mathematically that we couldn&amp;#8217;t do in Ruby. What it does do is let us accept a string and only perform this calculation if the string conforms to our grammar. So, if, say, you were writing a web application and wanted to allow your users to perform arithmetic on your website, but for some reason you didn&amp;#8217;t feel like doing this via&lt;sup class=&quot;footnote&quot;&gt;&lt;a href=&quot;#fn2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;code&gt;&amp;lt;%= eval(params[:arithmetic_expression]) %&amp;gt;&lt;/code&gt;, something like an ANTLR grammar might let you nicely sandbox and sanitize your user&amp;#8217;s inputs. And, &lt;code&gt;Expr.g&lt;/code&gt; might not be able to do anything all that fancy, but a more complex grammar might give you (and your untrustworthy users) some really interesting functionality.&lt;/p&gt;
&lt;p&gt;So, to recap, &lt;code&gt;Expr.string_parser&lt;/code&gt; returns an object of class &lt;code&gt;Expr&lt;/code&gt;, a Ruby class. We can call any of the grammar&amp;#8217;s rules as a method on that object and they will parse the string according to that rule, and the method will return a value if the rule returns a value.&lt;/p&gt;
&lt;p&gt;After we have done the parsing, we can call &lt;code&gt;parser.output&lt;/code&gt; to see what&amp;#8217;s in the output buffer. This will either be an array of strings or &lt;code&gt;nil&lt;/code&gt;. We can also call &lt;code&gt;parser.parser&lt;/code&gt; or &lt;code&gt;parser.lexer&lt;/code&gt; to return the &lt;code&gt;ExprParser&lt;/code&gt; or &lt;code&gt;ExprLexer&lt;/code&gt;, the actual ANTLR objects which do the parsing. Remember Expr is just our wrapper class. We can even call &lt;code&gt;parser.token_stream&lt;/code&gt;. And, since this is JRuby, we can have some introspection fun with these objects:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;already_parsed_rule
atom
backtracking_level
begin_resync
consume_until
display_recognition_error
emit_error_message
end_resync
equals
expr
grammar_file_name
hash_code
input
java_class
java_object
match
match_any
memoize
mismatch_is_missing_token
mismatch_is_unwanted_token
mult_expr
notify
notify_all
number_of_syntax_errors
prog
recover
recover_from_mismatched_set
report_error
reset
rule_invocation_stack
rule_memoization_cache_size
source_name
stat
synchronized
to_java_object
to_string
to_strings
token_names
token_stream
trace_in
trace_out
wait
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;consume
discard_off_channel_tokens
discard_token_type
equals
get
hash_code
index
java_class
java_object
la
lt
mark
notify
notify_all
release
reset
rewind
seek
size
source_name
synchronized
to_java_object
to_string
token_source
tokens
wait
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java_methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;already_parsed_rule
backtracking_level
begin_resync
char_error_display
char_index
char_position_in_line
char_stream
consume_until
display_recognition_error
emit
emit_error_message
end_resync
equals
grammar_file_name
hash_code
java_class
java_object
line
m_id
m_int
m_newline
m_t__10
m_t__11
m_t__12
m_t__13
m_t__8
m_t__9
m_tokens
m_ws
match
match_any
match_range
memoize
mismatch_is_missing_token
mismatch_is_unwanted_token
next_token
notify
notify_all
number_of_syntax_errors
recover
recover_from_mismatched_set
report_error
reset
rule_invocation_stack
rule_memoization_cache_size
skip
source_name
synchronized
text
to_java_object
to_string
to_strings
token_names
trace_in
trace_out
wait
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, here we have the internals of ANTLR laid out. You can see the values of any of these methods. Play with them. Use them for testing. Go into an interactive &lt;code&gt;jirb&lt;/code&gt; session and follow the lexing and parsing step-by-step until you know exactly how the grammar does its thing.&lt;/p&gt;
&lt;p&gt;By the way, AntlrVelvet uses &lt;code&gt;method_missing&lt;/code&gt; to pass method calls like &lt;code&gt;prog&lt;/code&gt; and &lt;code&gt;stat&lt;/code&gt; on to the embedded ExprParser object, but you could also explicitly define any of these methods and call the ExprParser yourself. This can be useful if you want to do some Ruby post-processing or perhaps debugging. If you do this, you will need to invoke @AntlrVelvet@&amp;#8217;s &lt;code&gt;capture_java_streams&lt;/code&gt; yourself if you don&amp;#8217;t want Java writing to your console. And remember, in JRuby Java&amp;#8217;s System.out and Ruby&amp;#8217;s $stdout are totally independent of each other. So if you have Ruby&amp;#8217;s $stdout redirected to a logfile, Java&amp;#8217;s System.out will still write to the console unless it, too, has been redirected.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Expr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;atom&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Your atom is &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;99&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atom&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;Your atom is 99
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On to testing! I&amp;#8217;m just using Ruby&amp;#8217;s Test::Unit for now. To begin with, I define some custom assertions and convenience methods:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_nothing_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_something_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_valid_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_nothing_raised&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;assert_nothing_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_too_much_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_something_left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_invalid_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;AntlrError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parsed_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parser_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Expr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To recap the examples from earlier in test form:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_prog_without_newline&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_invalid_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_prog_with_newline&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_valid_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_expr&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsed_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_stat_output&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_prog_output&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;     1 + 1&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;     x = 1&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;     y = 2&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;     3*(x+y)&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;     &amp;quot;&lt;/span&gt;

     &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;9&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:prog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
   
   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_nasty_expr&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3839&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parsed_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:expr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;1 + 2 * (3 + 4 * (5 + 6 * (7 + 8 * 9)))&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;assert_valid_input&lt;/code&gt;, &lt;code&gt;assert_invalid_input&lt;/code&gt; and &lt;code&gt;assert_too_much_input&lt;/code&gt; just take a symbol and an input string for the parser. &lt;code&gt;assert_invalid_input&lt;/code&gt; expects the parsing to throw an AntlrError, meaning that ANTLR wrote something to Java&amp;#8217;s System.err. This is used when you want to make sure that the input string is illegal for the rule in question, either too short or containing illegal characters or sequences. &lt;code&gt;assert_too_much_input&lt;/code&gt; means that the input is acceptable, but after processing there&amp;#8217;s something left over. In order for &lt;code&gt;assert_valid_input&lt;/code&gt; to pass, ANTLR must not find any errors and the input string must be entirely consumed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;parsed_value&lt;/code&gt; and &lt;code&gt;parser_output&lt;/code&gt; also take a symbol and an input string as arguments. They parse the string and return the grammar rule&amp;#8217;s return value or the text written to System.out respectively. Then you can write assertions for what the value or output should be, as shown. &lt;code&gt;assert_nothing_left&lt;/code&gt; and &lt;code&gt;assert_something_left&lt;/code&gt; are mostly for internal use, you have to initialize and run a parser yourself before passing the parser in to these functions, but of course you can call these directly if you want to.&lt;/p&gt;
&lt;p&gt;I use ANTLR&amp;#8217;s Java target because, as of this writing, the Ruby target for ANTLRv3 isn&amp;#8217;t complete enough to be practical. Even though I&amp;#8217;m not a fan of Java, I have to say that I really don&amp;#8217;t mind the little bit of it I have to write in a ANTLR grammar. If you are a Ruby programmer, don&amp;#8217;t let a bit of Java scare you away from the possibilities that ANTLR opens up. And, if you are a Java programmer, I hope this has demonstrated that JRuby can be a useful complement to Java, giving you a fantastic scripting language and testing environment for development, even if it doesn&amp;#8217;t play any role in your final product.&lt;/p&gt;
&lt;p&gt;As usual, all the source code is available for download from the sidebar. If you have any difficulty running any of the files, please let me know in the comments.&lt;/p&gt;
&lt;p class=&quot;footnote small&quot; id=&quot;fn1&quot;&gt;&lt;sup&gt;1&lt;/sup&gt; However, if you want to write a &lt;a href=&quot;http://www.theonion.com/content/node/37408&quot;&gt;syndicated advice column protocol&lt;/a&gt; then you might want a more state-machine-oriented tool like &lt;a href=&quot;http://research.cs.queensu.ca/~thurston/ragel/&quot;&gt;Ragel&lt;/a&gt;.&lt;/p&gt;
&lt;p class=&quot;footnote small&quot; id=&quot;fn2&quot;&gt;&lt;sup&gt;2&lt;/sup&gt; Joke!! I&amp;#8217;m KIDDING people!! You really think I would put something so unsafe in my code? Of course I meant &amp;lt;%=&lt;span style=&quot;color:red&quot;&gt;h&lt;/span&gt; eval(params[:arithmetic_expression]) %&amp;gt;.&lt;/p&gt;
    </content>
  </entry>
  
</feed>

