<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Random Hacks</title>
    <link>http://www.randomhacks.net/</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Technology and Other Fun Stuff</description>
    <item>
      <title>Visualizing WordNet relationships as graphs</title>
      <description>&lt;p&gt;The &lt;a href="http://wordnet.princeton.edu/"&gt;WordNet&lt;/a&gt; database contains all sorts of interesting relationships between words: it can categorize words into hierarchies, find the parts of an object, and answer many other interesting questions.&lt;/p&gt;

&lt;p&gt;The code below relies on the &lt;a href="http://www.nltk.org/"&gt;NLTK&lt;/a&gt; and &lt;a href="http://networkx.lanl.gov/"&gt;NetworkX&lt;/a&gt; libraries for Python.&lt;/p&gt;

&lt;h3&gt;Categorizing words&lt;/h3&gt;

&lt;p&gt;What, exactly, is a dog? It&amp;#8217;s a domestic animal and a carnivore, not to mention a physical entity (as opposed to an abstract entity, such as an idea). WordNet knows all these facts:&lt;/p&gt;

&lt;p&gt;&lt;a href="/files/dog.png"&gt;&lt;img src="/files/dog.png" width="406" height="306" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do we generate this image? First, we look up the first entry for &amp;#8220;dog&amp;#8221; in WordNet. This returns a &amp;#8220;synset&amp;#8221;, or a set of words with equivalent meanings.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;dog = wn.synset('dog.n.01')&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we compute the &lt;a href="http://en.wikipedia.org/wiki/Transitive_closure"&gt;transitive closure&lt;/a&gt; of the &lt;a href="http://en.wikipedia.org/wiki/Hyponymy"&gt;hypernym&lt;/a&gt; relationship, or (in English) we look for all the categories to which &amp;#8220;dog&amp;#8221; belongs, and all the categories to which &lt;em&gt;those&lt;/em&gt; categories belong, recursively:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;graph = closure_graph(dog,
                      lambda s: s.hypernyms())&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After that, we just pass the resulting graph to &lt;a href="http://networkx.lanl.gov/"&gt;NetworkX&lt;/a&gt; for display:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;nx.draw_graphviz(graph)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;The implementation&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;closure_graph&lt;/code&gt; function repeatedly calls &lt;code&gt;fn&lt;/code&gt; on the supplied symset, and uses the result to build a &lt;a href="http://networkx.lanl.gov/"&gt;NetworkX&lt;/a&gt; graph. This code goes at the top of the file, so you can use &lt;code&gt;wn&lt;/code&gt; and &lt;code&gt;nx&lt;/code&gt; in your own code.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;from nltk.corpus import wordnet as wn
import networkx as nx

def closure_graph(synset, fn):
    seen = set()
    graph = nx.DiGraph()

    def recurse(s):
        if not s in seen:
            seen.add(s)
            graph.add_node(s.name)
            for s1 in fn(s):
                graph.add_node(s1.name)
                graph.add_edge(s.name, s1.name)
                recurse(s1)

    recurse(synset)
    return graph&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By using a high-quality graph library, we make it much easier to merge, analyze and display our graphs.&lt;/p&gt;

&lt;h3&gt;More graphs&lt;/h3&gt;

&lt;p&gt;Parts of the finger, generated with &lt;code&gt;synset('finger.n.01')&lt;/code&gt; and &lt;code&gt;part_meronyms&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="/files/wn_finger.png"&gt;&lt;img src="/files/wn_finger.png" width="406" height="306" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Types of running, generated with &lt;code&gt;synset('run.v.01')&lt;/code&gt; and &lt;code&gt;hyponyms&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="/files/wn_run.png"&gt;&lt;img src="/files/wn_run.png" width="406" height="306" /&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 29 Dec 2009 20:38:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:bf20469d-bdce-4636-a7f1-33579f49b54c</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/12/29/visualizing-wordnet-relationships-as-graphs</link>
      <category>Python</category>
      <category>NLP</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/739</trackback:ping>
    </item>
    <item>
      <title>Experimenting with NLTK</title>
      <description>&lt;p&gt;The &lt;a href="http://www.nltk.org/"&gt;Natural Language Toolkit&lt;/a&gt; for Python is a great framework for simple, non-probabilistic natural language processing. Here are some example snippets (and some trouble-shooting notes).&lt;/p&gt;

&lt;h3&gt;Concordances&lt;/h3&gt;

&lt;p&gt;We can search for &amp;#8220;dog&amp;#8221; in &lt;a href="http://www.gutenberg.org/etext/1695"&gt;Chesterton&amp;#8217;s &lt;em&gt;The Man Who Was Thursday&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&amp;gt;&amp;gt;&amp;gt; from nltk.book import *
&amp;gt;&amp;gt;&amp;gt; text9.concordance(&amp;quot;dog&amp;quot;, width=40)
Displaying 4 of 4 matches:
ead of a cat or a dog , it could not ha
d you ever hear a dog bark like that ?&amp;quot;
aid , &amp;quot; is that a dog -- anybody ' s do
og -- anybody ' s dog ?&amp;quot; There broke up&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Synonyms and categories&lt;/h3&gt;

&lt;p&gt;We can use WordNet to look up synonyms:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;from nltk.corpus import wordnet

dog = wordnet.synset('dog.n.01')
print dog.lemma_names&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This prints:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;['dog', 'domestic_dog', 'Canis_familiaris']&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can also look up the &amp;#8220;hypernyms&amp;#8221;, or larger categories that include the word &amp;#8220;dog&amp;#8221;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;paths = dog.hypernym_paths()

def simple_path(path):
    return [s.lemmas[0].name for s in path]

for path in paths:
    print simple_path(path)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This prints:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_python "&gt;['entity', 'physical_entity', 'object',
 'whole', 'living_thing', 'organism',
 'animal', 'domestic_animal', 'dog']
['entity', 'physical_entity', 'object',
 'whole', 'living_thing', 'organism',
 'animal', 'chordate', 'vertebrate',
 'mammal', 'placental', 'carnivore',
 'canine', 'dog']&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For more neat examples, take a look at the &lt;a href="http://www.nltk.org/book"&gt;NLTK book&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Installation notes&lt;/h3&gt;

&lt;p&gt;While setting up NLTK, I bumped into a few problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The &lt;code&gt;dispersion_plot&lt;/code&gt; function returns immediately without displaying anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;a href="http://matplotlib.sourceforge.net/users/shell.html#mpl-shell"&gt;Configure your matplotlib back-end correctly.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The &lt;code&gt;nltk.app.concordance()&lt;/code&gt; GUI fails with the error:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;out of stack space (infinite loop?)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;a href="http://code.google.com/p/nltk/issues/detail?id=445"&gt;Recompile Tcl with threads.&lt;/a&gt; On the Mac:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;sudo port install tcl +threads&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <pubDate>Mon, 28 Dec 2009 21:31:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:d23d8142-890b-4aed-ad8c-38b73ffc102a</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/12/28/experimenting-with-nltk</link>
      <category>Python</category>
      <category>NLP</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/738</trackback:ping>
    </item>
    <item>
      <title>Interesting Python libraries for natural language processing</title>
      <description>&lt;p&gt;I&amp;#8217;ve been looking at various libraries for natural language processing, and I&amp;#8217;m pleasantly surprised by the tools created by the Python community. Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Python &lt;a href="http://www.nltk.org/"&gt;NLTK&lt;/a&gt; library provides parsers for many popular copora, visualization tools, and a wide variety of simple natural language algorithms (though few of these are probabilistic). Highlights include:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://wordnet.princeton.edu/"&gt;WordNet&lt;/a&gt; support.&lt;/li&gt;
&lt;li&gt;NumPy integration (see below).&lt;/li&gt;
&lt;li&gt;An accessible &lt;a href="http://www.nltk.org/book"&gt;introductory book on natural language processing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://conceptnet.media.mit.edu/"&gt;ConceptNet&lt;/a&gt; provides a simple semantic model of the world.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://numpy.scipy.org/"&gt;NumPy&lt;/a&gt; (and &lt;a href="http://www.scipy.org/"&gt;SciPy&lt;/a&gt;) provide extensive support for linear algebra and data visualization.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://mathema.tician.de/software/pycuda"&gt;PyCUDA&lt;/a&gt; provides access to Nvidia GPUs for high-performance scientific computation, and it integrates with NumPy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need to build a web crawler, there&amp;#8217;s &lt;a href="http://twistedmatrix.com/trac/"&gt;Twisted&lt;/a&gt;, which makes it easy to write fast, asynchronous networking code.&lt;/p&gt;

&lt;p&gt;All in all, I usually prefer Ruby to Python, because I love &lt;a href="http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp"&gt;Ruby&amp;#8217;s metaprogramming support&lt;/a&gt;. But the Python community has built an impressive variety of scientific and linguistic tools. Many thanks to everybody who contributed to these projects!&lt;/p&gt;</description>
      <pubDate>Mon, 28 Dec 2009 15:56:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:41c8c423-bcb5-4d98-bb64-ede7ca42792f</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/12/28/interesting-python-libraries-for-natural-language-processing</link>
      <category>Python</category>
      <category>NLP</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/737</trackback:ping>
    </item>
    <item>
      <title>Wave Hackathon</title>
      <description>&lt;p&gt;I&amp;#8217;m currently attending the Wave hackathon at the &lt;a href="http://massgtug.gtugs.org/"&gt;Massachusetts GTUG&lt;/a&gt;. Here&amp;#8217;s some code from a &lt;a href="http://www.waveprotocol.org/"&gt;protocol-level&lt;/a&gt; Wave agent that I just demoed:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# Capitalize random words.&lt;/span&gt;
&lt;span class="ident"&gt;replace&lt;/span&gt; &lt;span class="punct"&gt;/\&lt;/span&gt;&lt;span class="ident"&gt;b&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;random&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;words&lt;/span&gt;&lt;span class="punct"&gt;)\&lt;/span&gt;&lt;span class="ident"&gt;b&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
  &lt;span class="ident"&gt;word&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;upcase&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="comment"&gt;# Shorten URLs.&lt;/span&gt;
&lt;span class="ident"&gt;replace&lt;/span&gt; &lt;span class="punct"&gt;/\&lt;/span&gt;&lt;span class="ident"&gt;bhttp&lt;/span&gt;&lt;span class="punct"&gt;:\/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\/&lt;/span&gt;([^ ]+)&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;  &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;url&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; 
  &lt;span class="global"&gt;$bitly&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;shorten&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;url&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;short_url&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In keeping with the traditions of hackathons, this agent is horribly fragile. It only works with FedOne&amp;#8217;s console-based wave client, and it doesn&amp;#8217;t handle annotations correctly.&lt;/p&gt;

&lt;p&gt;Some earlier—and more robust—wave-related projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://wave-samples-gallery.appspot.com/about_app?app_id=66001"&gt;Pick Several&lt;/a&gt;: A gadget which implements approval voting. Written using GWT. Includes a reusable library for writing GWT-based wave gadgets.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wave-samples-gallery.appspot.com/about_app?app_id=27027"&gt;BugLinky&lt;/a&gt;: A robot which links bug numbers to a bug tracker. Includes a reusable library for simple pattern-matching, text replacement and annotation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many thanks to GTUG and to Google for organizing this hackathon!&lt;/p&gt;</description>
      <pubDate>Sat, 21 Nov 2009 23:35:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:2137e369-c5d9-4702-ad99-886595df73e2</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/11/21/wave-hackathon</link>
      <category>Wave</category>
      <category>Ruby</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/734</trackback:ping>
    </item>
    <item>
      <title>Upgrading randomhacks.net</title>
      <description>&lt;p&gt;I&amp;#8217;m currently upgrading randomhacks.net, and you may encounter some RSS-related weirdness. Also, some programs in locally-hosted version control repositories may be unavailable temporarily.&lt;/p&gt;

&lt;p&gt;My apologies for any inconveniences. I hope to post some more Ruby and Haskell code soon!&lt;/p&gt;</description>
      <pubDate>Sat, 12 Sep 2009 00:37:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:6b8a1b94-4109-44e0-82c3-a741b49ba0e2</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/09/12/upgrading-randomhacks-net</link>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/711</trackback:ping>
    </item>
    <item>
      <title>Real-time text annotation with Google Wave</title>
      <description>&lt;p&gt;If you haven&amp;#8217;t seen Google Wave yet, you may want to watch this &lt;a href="http://www.youtube.com/watch?v=Itc4253kjhw"&gt;10-minute demo&lt;/a&gt; or take a look at the &lt;a href="http://wave.google.com/"&gt;official Wave site&lt;/a&gt;. Otherwise, this code won&amp;#8217;t make a lot of sense. :-)&lt;/p&gt;

&lt;p&gt;&lt;img style="border: 1px solid grey; margin-left: 50px" src="/files/buglinky-screenshot-large.png" width="300" height="401" /&gt;&lt;/p&gt;

&lt;p style="margin-left: 50px"&gt;&lt;b&gt;Figure:&lt;/b&gt; Real-time text annotation with Google Wave.&lt;/p&gt;

&lt;p&gt;I received my developer sandbox account last week, and spent some time experimenting with Wave. So far, it seems like a really sweet tool—it&amp;#8217;s fast (if you stay away from waves with 200+ messages), convenient, and fun to use. Wave isn&amp;#8217;t yet ready for prime time, but it could be fairly solid in six months and widely deployed by this time next year.&lt;/p&gt;

&lt;p&gt;After using Wave for less than a week, I &lt;em&gt;really&lt;/em&gt; wish my friends and coworkers had accounts. This is a good sign.&lt;/p&gt;

&lt;p&gt;Google has already released about 40,000 lines of Wave code as open source, and there&amp;#8217;s apparently quite a bit more in the pipeline.&lt;/p&gt;

&lt;h3&gt;Extending wave with gadgets and robots&lt;/h3&gt;

&lt;p&gt;Wave supports two major kinds of extensions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Gadgets&lt;/em&gt; are interactive content that users can embed in a wave. In the &lt;a href="http://www.youtube.com/watch?v=Itc4253kjhw"&gt;demo&lt;/a&gt;, Google shows off a map gadget, a chess game, a handy &amp;#8220;Yes/No/Maybe&amp;#8221; poll, and many others.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Robots&lt;/em&gt; are essentially a cross between IRC bots, text editor extensions, and web form processors. They can do anything another human could do, in real-time, as you type. They can also create and respond to HTML-style form elements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So far, I&amp;#8217;ve tried writing a robot in Java. (Why Java? Wave is built using &lt;a href="http://code.google.com/webtoolkit/"&gt;Google Web Toolkit&lt;/a&gt;, which compiles Java to JavaScript. So right now, the Java libraries are slightly more mature than the Python libraries.)&lt;/p&gt;

&lt;h3&gt;buglinky: Link to bugs as you type&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://wave-samples-gallery.appspot.com/about_app?app_id=27027"&gt;buglinky&lt;/a&gt; reads your text as you type, and automatically links strings of the form &amp;#8220;bug #123&amp;#8221; to a bug tracker of your choice. It can also detect raw bug tracker URLs and replace them with the corresponding text.&lt;/p&gt;

&lt;p&gt;Internally, buglinky is built around a custom &lt;code&gt;BlipProcessor&lt;/code&gt; class that does all the heavy lifting. All I need to do is hook up my individual processors and run them:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_java "&gt;ArrayList&amp;lt;BlipProcessor&amp;gt; processors =
  new ArrayList&amp;lt;BlipProcessor&amp;gt;();
processors.add(new BugUrlReplacer(BUG_URL));
processors.add(new BugNumberLinker(BUG_URL));
BlipProcessor.applyProcessorsToChangedBlips(
  processors, bundle, BOT_ADDRESS);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;#8217;s the code which links &amp;#8220;bug #123&amp;#8221; to the bug tracker:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_java "&gt;class BugNumberLinker extends BlipProcessor {
  // ...constructor sets up bugUrl...

  protected String getPattern() {
    return &amp;quot;(?:[Bb]ug|[Ii]ssue|[Tt]icket|[Cc]ase) \#?(\\d+)&amp;quot;;
  }

  protected void processMatch(
      TextView doc, Range range, Matcher match) {
    annotate(doc, range, &amp;quot;link/manual&amp;quot;,
      bugUrl.concat(match.group(1)));
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Replacing URLs with plain text is similarly easy:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_java "&gt;class BugUrlReplacer extends BlipProcessor {
  // ...constructor sets up bugUrl...

  protected String getPattern() {
    return Pattern.quote(bugUrl) + &amp;quot;(\\d+)&amp;quot;;
  }

  protected void processMatch(
      TextView doc, Range range, Matcher match) {
    replace(doc, range, &amp;quot;issue #&amp;quot; + match.group(1));
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you have a sandbox account, you can &lt;a href="https://wave.google.com/a/wavesandbox.com/#restored:wave:wavesandbox.com!w%252B06QQ6YMY%2525C.2"&gt;experiment with buglinky&lt;/a&gt;. You can also &lt;a href="http://github.com/emk/buglinky/tree/master"&gt;download the source code for buglinky from github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Future directions&lt;/h3&gt;

&lt;p&gt;Obviously, this will look much better once somebody has the time to port it to JRuby. How about something like this?&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;match&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="comment"&gt;#{Regexp.escape(bugUrl)}(\\d+)/ do&lt;/span&gt;
  &lt;span class="ident"&gt;replace&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;issue ##&lt;span class="expr"&gt;#{$1}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;match&lt;/span&gt; &lt;span class="punct"&gt;/(&lt;/span&gt;&lt;span class="char"&gt;?:&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;Bb&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;span class="ident"&gt;ug&lt;/span&gt;&lt;span class="punct"&gt;|[&lt;/span&gt;&lt;span class="constant"&gt;Ii&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;span class="ident"&gt;ssue&lt;/span&gt;&lt;span class="punct"&gt;|[&lt;/span&gt;&lt;span class="constant"&gt;Tt&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;span class="ident"&gt;icket&lt;/span&gt;&lt;span class="punct"&gt;|[&lt;/span&gt;&lt;span class="constant"&gt;Cc&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;span class="ident"&gt;ase&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; \&lt;span class="comment"&gt;#?(\\d+)/ do&lt;/span&gt;
  &lt;span class="ident"&gt;link&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;bug_url&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additionally, the current robot API relies on a JSON-RPC-based proxy between the Wave server and a robot. This is fine for processing entire waves in a single pass, but it uses too much bandwidth for real-time text processing. So I would love to be able to run this code inside a real wave server. But that will have to wait until the federation protocol is turned on.&lt;/p&gt;</description>
      <pubDate>Tue, 01 Sep 2009 13:06:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:fb7773df-9b8b-4db5-9098-217d9c628bf8</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/09/01/real-time-text-annotation-with-google-wave</link>
      <category>Wave</category>
      <category>Java</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/710</trackback:ping>
    </item>
    <item>
      <title>Write a 32-line chat client using Ruby, AMQP &amp;amp; EventMachine (and a GUI using Shoes)</title>
      <description>&lt;p&gt;Have you ever considered using instant messages to communicate between programs? You can do this using &lt;a href="http://www.jabber.org/"&gt;Jabber&amp;#8217;s XMPP protocol&lt;/a&gt;, of course. But it&amp;#8217;s also worth taking a look at &lt;a href="http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol"&gt;AMQP&lt;/a&gt;, a distributed messaging protocol first used at JPMorgan Chase. AMQP is fast, easy to use, and implemented by at least 4 open source servers.&lt;/p&gt;

&lt;p&gt;To try it out, install the excellent &lt;a href="http://github.com/tmm1/amqp/tree/master"&gt;Ruby AMQP bindings&lt;/a&gt;, and set up the &lt;a href="http://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt; server (which is written in Erlang using Mnesia). On a Mac, you might do something like this:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;sudo gem install amqp
sudo port install python25 rabbitmq-server
sudo rabbitmq-server&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once your server is running, save the following code as &lt;code&gt;chat.rb&lt;/code&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rubygems&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;gem&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;amqp&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;mq&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;ARGV&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt;
  &lt;span class="constant"&gt;STDERR&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Usage: &lt;span class="expr"&gt;#{$0}&lt;/span&gt; &amp;lt;channel&amp;gt; &amp;lt;nick&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="global"&gt;$channel&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="global"&gt;$nick&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ARGV&lt;/span&gt;

&lt;span class="constant"&gt;AMQP&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;start&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:host&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;localhost&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="global"&gt;$chat&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;MQ&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;topic&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;chat&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;

  &lt;span class="comment"&gt;# Print any messages on our channel.&lt;/span&gt;
  &lt;span class="ident"&gt;queue&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;MQ&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="global"&gt;$nick&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;chat&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:key&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="global"&gt;$channel&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;subscribe&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;msg&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;msg&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{$nick}&lt;/span&gt;:&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="punct"&gt;!=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
      &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;msg&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# Forward console input to our channel.&lt;/span&gt;
  &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;KeyboardInput&lt;/span&gt;
    &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;EM&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Protocols&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;LineText2&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;receive_line&lt;/span&gt; &lt;span class="ident"&gt;data&lt;/span&gt;
      &lt;span class="global"&gt;$chat&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;publish&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{$nick}&lt;/span&gt;: &lt;span class="expr"&gt;#{data}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;
                    &lt;span class="symbol"&gt;:routing_key&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="global"&gt;$channel&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="constant"&gt;EM&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;open_keyboard&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;KeyboardInput&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, run copies in two different terminals:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;ruby chat.rb channel_1 sarah
ruby chat.rb channel_1 joe&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Everything you type into one terminal will be relayed to the other.&lt;/p&gt;

&lt;h3&gt;How it works&lt;/h3&gt;

&lt;p&gt;The following line creates a &lt;em&gt;topic exchange&lt;/em&gt; named &amp;#8220;chat&amp;#8221;: &lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="global"&gt;$chat&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;MQ&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;topic&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;chat&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A topic exchange allows many-to-many communication.  Here, we bind a listener to our exchange, and ask to receive all messages tagged with our channel name:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;chat&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:key&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="global"&gt;$channel&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;:key&lt;/code&gt; may be hierarchical, and it may contain wildcards. To write data to our topic exchange, we use &lt;code&gt;publish&lt;/code&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="global"&gt;$chat&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;publish&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{$nick}&lt;/span&gt;: &lt;span class="expr"&gt;#{data}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt;
              &lt;span class="symbol"&gt;:routing_key&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="global"&gt;$channel&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our keyboard input is processed using &lt;a href="http://rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt;, a Ruby library for writing high-performance, multi-protocol servers. It&amp;#8217;s very similar to Python&amp;#8217;s &lt;a href="http://twistedmatrix.com/trac/"&gt;Twisted&lt;/a&gt; library, though it has less documentation and support for fewer protocols.&lt;/p&gt;

&lt;p&gt;We use EventMachine&amp;#8217;s &lt;code&gt;EM.open_keyboard&lt;/code&gt; to create a asynchronous keyboard input channel, and we use &lt;code&gt;EM::Protocols::LineText2&lt;/code&gt; to treat the keyboard input as a line-oriented protocol.&lt;/p&gt;

&lt;h3&gt;Adding a Shoes GUI&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://shoooes.net/"&gt;Shoes&lt;/a&gt; is an eccentric, entertaining, and highly-portable GUI library by &lt;a href="http://whytheluckystiff.net/"&gt;_why the lucky stiff&lt;/a&gt;. With a certain amount of grotesque kludging (and some pointers from &amp;#8220;s1kx&amp;#8221; on the #shoes IRC channel), I managed to get the Mac version of Shoes to talk to EventMachine. You may find that this code fails strangely on your computer. Honestly, I don&amp;#8217;t know anything about Shoes. And I&amp;#8217;m doing some pretty bad things with threads.&lt;/p&gt;

&lt;p&gt;First, the pretty pictures:&lt;/p&gt;

&lt;p&gt;&lt;img src="/files/shoes-chat.png" width="318" height="297" /&gt;&lt;/p&gt;

&lt;p&gt;Next, the code:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;Shoes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;setup&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="ident"&gt;gem&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;amqp&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;mq&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="global"&gt;$app&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Shoes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;app&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:width&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;256&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;background&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;gradient&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;#CFF&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#FFF&lt;/span&gt;&lt;span class="punct"&gt;'))&lt;/span&gt;
  &lt;span class="attribute"&gt;@output&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;stack&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:margin&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;10&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;nick&lt;/span&gt; &lt;span class="ident"&gt;str&lt;/span&gt;
    &lt;span class="ident"&gt;span&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;str&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:stroke&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;red&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;display&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;
    &lt;span class="attribute"&gt;@output&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;append&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^([^:]+): (.*)$&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
        &lt;span class="ident"&gt;para&lt;/span&gt; &lt;span class="ident"&gt;nick&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{$1}&lt;/span&gt;: &lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;),&lt;/span&gt; &lt;span class="global"&gt;$2&lt;/span&gt;
      &lt;span class="keyword"&gt;else&lt;/span&gt;
        &lt;span class="ident"&gt;para&lt;/span&gt; &lt;span class="ident"&gt;text&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="constant"&gt;Thread&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="keyword"&gt;begin&lt;/span&gt;
    &lt;span class="constant"&gt;AMQP&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;start&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:host&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;localhost&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="constant"&gt;MQ&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;topic&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;chat&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
      &lt;span class="ident"&gt;queue&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;MQ&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;shoes&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
      &lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;bind&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;chat&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
      &lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;subscribe&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;msg&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="global"&gt;$app&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;display&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;msg&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;
    &lt;span class="comment"&gt;# Try to report at least _some_ errors&lt;/span&gt;
    &lt;span class="comment"&gt;# where we'll be able to see them.&lt;/span&gt;
    &lt;span class="global"&gt;$app&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;display&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the GUI client listens to all channels simultaneously, because it doesn&amp;#8217;t pass a &lt;code&gt;:key&lt;/code&gt; to &lt;code&gt;bind&lt;/code&gt;. And when writing code to run in a Shoes background thread, don&amp;#8217;t expect to see any error messages.&lt;/p&gt;

&lt;h3&gt;Learning more about AMQP&lt;/h3&gt;

&lt;p&gt;The &lt;a href="http://amqp.rubyforge.org/"&gt;Ruby AMQP documentation page&lt;/a&gt; has a good list of papers, magazine articles, and other background material on AMQP.&lt;/p&gt;</description>
      <pubDate>Fri, 08 May 2009 18:06:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:1119e725-a4de-4e5b-a211-8405e7490eeb</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes</link>
      <category>Ruby</category>
      <category>MQ</category>
      <category>AMQP</category>
      <category>EventMachine</category>
      <category>Shoes</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/620</trackback:ping>
    </item>
    <item>
      <title>Financial crisis background and Munger on the banks</title>
      <description>&lt;p&gt;Charlie Munger is the long-time partner of Warren Buffet. Of the two, he&amp;#8217;s the more politically conservative. Their company, Berkshire Hathaway, has recently bought big stakes in several of the better-off investment banks.&lt;/p&gt;

&lt;p&gt;Recently, &lt;a href="http://www.bloomberg.com/apps/news?pid=20601087&amp;amp;sid=aRGmF1WqsCgA&amp;amp;refer=home"&gt;Munger sharply criticized the management of the investment banks&lt;/a&gt;, saying they&amp;#8217;ve grown too politically powerful. The key quote:&lt;/p&gt;

&lt;blockquote&gt;“We need to remove from the investment banking and the commercial banking industries a lot of the practices and prerogatives that they have so lovingly possessed,” Munger said. “If they are too big to fail, they are too big to be allowed to be as gamey and venal as they’ve been &amp;#8211; and as stupid as they’ve been.” (Bloomberg.com, via &lt;a href="http://baselinescenario.com/2009/05/05/all-about-optics-predicting-stress-test-outcomes/"&gt;Baseline Scenario&lt;/a&gt;.)&lt;/blockquote&gt;

&lt;p&gt;What does the bankers&amp;#8217; stupidity have to do with the usual themes of this blog? Well, much of the crisis comes down to bad probability calculations: the big banks have been treating highly correlated events as though they were independent events.&lt;/p&gt;

&lt;p&gt;Some good background material on the crisis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A neat visual metaphor for CDOs: &lt;a href="http://vimeo.com/1876936?pg=embed&amp;amp;sec="&gt;Filling a tower of champagne glasses.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.khanacademy.org/"&gt;Khan Academy videos on the financial crisis&lt;/a&gt; (middle column, halfway down).&lt;/li&gt;
&lt;li&gt;A confession by the programmer who wrote &lt;a href="http://nymag.com/news/business/55687/"&gt;the software that encouraged this whole mess&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</description>
      <pubDate>Tue, 05 May 2009 09:36:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:5fc8fe5a-4afd-42c0-b21a-6a80077dc924</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/05/05/financial-crisis</link>
      <category>Probability</category>
      <category>Finance</category>
      <category>Politics</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/617</trackback:ping>
    </item>
    <item>
      <title>Designing programs with RSpec and Cucumber (plus a book recomendation)</title>
      <description>&lt;p&gt;Over the last couple of years, I&amp;#8217;ve occasionally written Ruby programs using &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt; and (more recently) &lt;a href="http://rspec.info/"&gt;Cucumber&lt;/a&gt;. These two tools are inspired by &lt;a href="http://www.amazon.com/Test-Driven-Development-Addison-Wesley-Signature/dp/0321146530"&gt;Test Driven Development&lt;/a&gt; (TDD), a school of thought which says you should write unit tests &lt;em&gt;before&lt;/em&gt; implementing a feature. &lt;/p&gt;

&lt;p&gt;When doing TDD, you work inwards from the interface to the implementation. You start by writing a test case against the interface you &lt;em&gt;wish&lt;/em&gt; you had, and then you make that test case work. This is a subtle shift in how you approach a design problem, but it frequently results in beautiful APIs. (And you also get a fully automated test suite for your software, liberating you to make much larger changes without fear of breaking things.)&lt;/p&gt;

&lt;h3&gt;The problem with the word &amp;#8220;test&amp;#8221;&lt;/h3&gt;

&lt;p&gt;Unfortunately, the name &amp;#8220;Test Driven Development&amp;#8221; is misleading. Most folks think of &amp;#8220;testing&amp;#8221; as something you do &lt;em&gt;after&lt;/em&gt; development is complete. But TDD is really more of a design activity&amp;#8212;you&amp;#8217;re specifying how your APIs should work &lt;em&gt;before&lt;/em&gt; you actually start coding.&lt;/p&gt;

&lt;p&gt;Dan North spent some time struggling to teach developers about TDD. After a while, he decided that the main barrier to understanding was the word &amp;#8220;test.&amp;#8221; He proposed replacing TDD with &lt;a href="http://dannorth.net/introducing-bdd"&gt;Behavior Driven Development&lt;/a&gt; (BDD), and he started referring to unit tests as &amp;#8220;specifications.&amp;#8221;&lt;/p&gt;

&lt;p&gt;In the Ruby community, the most popular BDD tool is &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt;. Using RSpec, you might specify an API something like this:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;simplify_name&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should convert all letters to lowercase&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;simplify_name&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;AbC&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;).&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;abc&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should remove everything but letters and spaces&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;simplify_name&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt; Joe Smith 3 -+&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;).&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;joe smith&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After writing this specification, you would then go ahead and implement &lt;code&gt;simplify_name&lt;/code&gt;. And from then on, whenever you changed your program, you could automatically check it against this specification.&lt;/p&gt;

&lt;h3&gt;Using specifications to communicate with clients and users&lt;/h3&gt;

&lt;p&gt;By itself, RSpec is mostly useful for programmers. Sure, a specification looks a lot like English. But would you really want to show it to an end user?&lt;/p&gt;

&lt;p&gt;&lt;a href="http://rspec.info/"&gt;Cucumber&lt;/a&gt; goes one step further. Instead of using code to specify how an API should work, it uses plain text to describe how a &lt;em&gt;user interface&lt;/em&gt; should work. For example:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_cucumber "&gt;Feature: Log in and out
  As an administrator
  I want to restrict access to certain portions of my site
  In order to prevent users from changing the content

  Scenario: Logging in
    Given I am not logged in as an administrator
    When I go to the administrative page
    And I fill in the fields
      | Username | admin  |
      | Password | secret |
    And I press &amp;quot;Log in&amp;quot;
    Then I should be on the administrative page
    And I should see &amp;quot;Log out&amp;quot;

  Scenario: Logging out
    ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;#8217;s the neat part: This specification is actually an executable program. Each line of text corresponds to a &amp;#8220;step&amp;#8221;, which is defined in another file. Here&amp;#8217;s an example from the standard &lt;code&gt;webrat_steps.rb&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;Then&lt;/span&gt; &lt;span class="punct"&gt;/^&lt;/span&gt;&lt;span class="constant"&gt;I&lt;/span&gt; &lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;see&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;([^&lt;span class="escape"&gt;\&amp;quot;&lt;/span&gt;]*)&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="global"&gt;$/&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
  &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;contain&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;text&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cucumber encourages you to think at a very high level, and to specify how different users will actually use your software. It&amp;#8217;s particularly helpful if you need to communicate between programmers and end-users.&lt;/p&gt;

&lt;h3&gt;My experiences with RSpec and Cucumber&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;ve been using RSpec on and off for a couple of years now, and Cucumber since late last year. Initially, I found both tools fascinating, but also a bit frustrating. Both RSpec and Cucumber have very strong opinions about how you should write software. Now, I found those opinions very interesting, and I was quite happy to be influenced by the assumptions built into the tools. But every now and then, I would need to do something that the authors of RSpec and Cucumber hadn&amp;#8217;t anticipated, and I would inevitably wind up struggling to make things work.&lt;/p&gt;

&lt;p&gt;But recent versions of RSpec and Cucumber are richer and more flexible. They cover more important cases straight out of the box, and they&amp;#8217;re easier to customize. So I can finally recommend both tools for real-world projects: They&amp;#8217;ll still guide your thinking, but they should give you enough flexibility to handle the corner-cases.&lt;/p&gt;

&lt;h3&gt;The RSpec (and Cucumber) book&lt;/h3&gt;

&lt;p&gt;Unfortunately, the documentation for RSpec and Cucumber is scattered around the web, and there aren&amp;#8217;t enough online guides showing the best way to solve common problems.&lt;/p&gt;

&lt;p&gt;But the Pragmatic Press is working on &lt;a href="http://www.pragprog.com/titles/achbd/the-rspec-book"&gt;The RSpec Book&lt;/a&gt;, which contains a large section on Cucumber, and a walkthrough of a typical development session using Cucumber and RSpec.&lt;/p&gt;

&lt;p&gt;Currently, the RSpec book is available as a &amp;#8220;beta book&amp;#8221;. This is a downloadable, DRM-free PDF, with periodic updates throughout the publishing process.  Right now, between one-third and one-half of the chapters have been roughed in, and the book is already very useful.&lt;/p&gt;

&lt;p&gt;So if you&amp;#8217;re curious about &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt; and &lt;a href="http://rspec.info/"&gt;Cucumber&lt;/a&gt;, have a look around the two web sites, and maybe watch some of the screencasts. If you decide to investigate further, pick up the beta book and dive in.&lt;/p&gt;</description>
      <pubDate>Thu, 30 Apr 2009 15:07:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:6e7a670a-6d09-430d-b788-da8135f1d8e2</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/04/30/rspec-cucumber-book-recommendation</link>
      <category>Ruby</category>
      <category>RSpec</category>
      <category>Cucumber</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/610</trackback:ping>
    </item>
    <item>
      <title>Remote root holes reported as &amp;quot;denial of service&amp;quot;</title>
      <description>&lt;p&gt;Via &lt;a href="http://lwn.net/Articles/330866/"&gt;LWN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re a Linux system administrator, you shouldn&amp;#8217;t put your faith in security advisories. The &lt;a href="http://kernelbof.blogspot.com/2009/04/kernel-memory-corruptions-are-not-just.html"&gt;kernelbof&lt;/a&gt; blog accuses Linux distributors of being too quick to label security bugs as &amp;#8220;denial of service&amp;#8221; attacks:&lt;/p&gt;

&lt;blockquote&gt;
I&amp;#8217;m wondering why kernel developers (or vendors?) continue to claim that kernel memory corruption are just Denial of Service. Most of the times they _are_ exploitable.
&lt;/blockquote&gt;

&lt;p&gt;As an example, the author quotes &lt;a href="http://www.ubuntu.com/usn/usn-751-1"&gt;Ubuntu Security Notice 751&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;The SCTP stack did not correctly validate FORWARD-TSN packets. A remote attacker could send specially crafted SCTP traffic causing a system crash, &lt;strong&gt;leading to a denial of service.&lt;/strong&gt;&lt;/blockquote&gt;

&lt;p&gt;(Emphasis added.)&lt;/p&gt;

&lt;p&gt;The author claims, however, to have created an exploit for this bug. He says his exploit allows a remote attacker to gain root access, often on the first attempt. If this is true, it would give him a quick way to gain control over any Linux system which has a process listening to an SCTP socket.&lt;/p&gt;

&lt;p&gt;Ubuntu&amp;#8217;s security team is not doing system administrators any favors by labeling memory corruption as &amp;#8220;denial of service&amp;#8221; attacks. If you can corrupt memory, there are some terrifyingly clever ways to run code. And marking memory as non-executable won&amp;#8217;t necessarily protect you.&lt;/p&gt;

&lt;p&gt;If you administer a Linux system, you should probably aim to patch alleged &amp;#8220;denial of service&amp;#8221; bugs as quickly as you can.&lt;/p&gt;</description>
      <pubDate>Thu, 30 Apr 2009 12:57:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:c25e2eee-9864-43d8-8b0b-6b0c0a4af6b1</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2009/04/30/remote-root-holes-reported-as-denial-of-service</link>
      <category>Linux</category>
      <category>Security</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/609</trackback:ping>
    </item>
  </channel>
</rss>
