<?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: Tag MQ</title>
    <link>http://www.randomhacks.net/articles/tag/MQ?tag=MQ</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Technology and Other Fun Stuff</description>
    <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>
  </channel>
</rss>

