<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Random Hacks: Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)</title>
    <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes</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; EventMachine (and a GUI using Shoes)" by TheCheshireCatalyst</title>
      <description>&lt;p&gt;Use&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/PSYC"&gt; PSYC &lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://about.psyc.eu/"&gt; And here &lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 11 Jun 2009 13:59:21 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:6a9647ce-d25f-4aa4-9132-b6341e5492f8</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-640</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by _why</title>
      <description>&lt;p&gt;Hey hello, nice bit of hacking you got there. I just wanted to apologize for the threading trouble, I try to tell experienced programmers to stay away from Shoes, since the &lt;span class="caps"&gt;GUI&lt;/span&gt; presently runs in the main thread and wrestles with Ruby for control. Yeah, bad stuff&amp;#8212;Shoes was intended for beginners and I&amp;#8217;ve had my hands full with all the platform code.&lt;/p&gt;


	&lt;p&gt;At any rate, I&amp;#8217;m working hard to switch us to Ruby 1.9 and changing the thread/memory situation in the process. Since the drawing primitives, music, animation and video are all cooked: now I can turn my attention to the robustness. This can&amp;#8217;t happen soon enough.&lt;/p&gt;


	&lt;p&gt;At any rate, very nice work, I may swap out Hackety Hack&amp;#8217;s messaging code for &lt;span class="caps"&gt;AMQP&lt;/span&gt;, it looks very swift and featureful.&lt;/p&gt;</description>
      <pubDate>Mon, 11 May 2009 13:36:37 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:895e76d4-2d99-4d5d-8e48-453b20bee749</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-629</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by Eric</title>
      <description>&lt;p&gt;riffraff: Thanks! I didn&amp;#8217;t know about &lt;code&gt;abort&lt;/code&gt;.&lt;/p&gt;


	&lt;p&gt;John Apps: Thank you for the link to &lt;a href="http://www.opensourcery.co.za/2009/04/19/to-amqp-or-to-xmpp-that-is-the-question/"&gt;To &lt;span class="caps"&gt;AMQP&lt;/span&gt; or to &lt;span class="caps"&gt;XMPP&lt;/span&gt;&lt;/a&gt;. That&amp;#8217;s a nice comparison of the two protocols.&lt;/p&gt;


	&lt;p&gt;I should mention, however, that RabbitMQ is ridiculously easy to install on any platform that has the appropriate packages. In fact, the RabbitMQ folks have a pretty admirable installation policy:&lt;/p&gt;


&lt;blockquote&gt;Our goal is to streamline the broker installation process such that you can have RabbitMQ up and running within two minutes of completing your download. If this doesn&amp;#8217;t happen to you, please let us know at legitimategrievance@rabbitmq.com!&lt;/blockquote&gt;</description>
      <pubDate>Sun, 10 May 2009 19:39:11 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:7a376dc4-c862-4632-9b0d-87be9bc19bf3</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-628</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by John Apps</title>
      <description>&lt;p&gt;Here is a good comparison of &lt;span class="caps"&gt;XMPP&lt;/span&gt; and &lt;span class="caps"&gt;AMQP&lt;/span&gt;: http://www.opensourcery.co.za/2009/04/19/to-amqp-or-to-xmpp-that-is-the-question/
&lt;span class="caps"&gt;AMQP&lt;/span&gt; is well summed up with the term &amp;#8220;enterprise messaging&amp;#8221; as it has all the attributes required for that kind of application. Lots of clients available: Ruby, Python, Java, .NET, C/C++, high-level languages, etc.&lt;/p&gt;


	&lt;p&gt;0MQ is a lot simpler with a great little &lt;span class="caps"&gt;API&lt;/span&gt; and blindingly fast. Very clean code &amp;#8211; lean and mean.&lt;/p&gt;</description>
      <pubDate>Sun, 10 May 2009 10:16:57 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:aa1e983f-44e3-4b9a-8826-f6580450d250</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-627</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by riffraff</title>
      <description>&lt;p&gt;cool, I need to try out the shoes gui though :D&lt;/p&gt;


	&lt;p&gt;PS:
you can shave a few lines e.g. 
 unless &lt;span class="caps"&gt;ARGV&lt;/span&gt;.length  2
  STDERR.puts "Usage: #{$0} &lt;channel&gt; &lt;nick&gt;" 
  exit 1
 end
with 
  abort(msg) unless ARGV.size 2&lt;/p&gt;


	&lt;p&gt;and
    if msg.index(&amp;#8221;#{$nick}:&amp;#8221;) != 0
      puts msg
    end
with
 puts(msg) unless msg[&amp;#8221;^#{nick}:&amp;#8221;]&lt;/p&gt;


	&lt;p&gt;;)&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2009 16:23:37 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:c6e07edc-4f35-4e39-bf4b-d30a6a13dc82</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-626</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by Eric</title>
      <description>&lt;p&gt;Astro: EventMachine has built-in support for threads. In particular, see &lt;a href="http://eventmachine.rubyforge.org/EventMachine.html#M000398"&gt;EventMachine.defer&lt;/a&gt;, which schedules blocking tasks using a thread pool, and &lt;a href="http://rubyeventmachine.com/wiki/FAQ#DoesEMworkwithotherRubythreadsrunning"&gt;the note in the &lt;span class="caps"&gt;FAQ&lt;/span&gt; on using EventMachine with other Ruby threads running&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;That said, I&amp;#8217;m reasonably certain that my Shoes/EventMachine code isn&amp;#8217;t thread-safe. Shoes is a very cute framework, but it has very little documentation, and it certainly isn&amp;#8217;t meant for running multithreaded programs. In my case, I&amp;#8217;m making &lt;span class="caps"&gt;GUI&lt;/span&gt; calls from two different green threads, which certainly might scramble the &lt;span class="caps"&gt;GUI&lt;/span&gt; state on certain platforms.&lt;/p&gt;


	&lt;p&gt;If I were going to turn this into a real program, I&amp;#8217;d try to use some simple locks to control access to the &lt;span class="caps"&gt;GUI&lt;/span&gt;, and I&amp;#8217;d definitely test it on more than one platform. But without reading the Shoes source code, I&amp;#8217;m basically just kludging around in the dark.&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2009 09:41:30 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:71eb4a8c-b86b-4454-8d7d-ab6070184bfe</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-625</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by Eric</title>
      <description>&lt;p&gt;Anon: The user names are made up.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;m not really the right person to ask about the relative merits of &lt;span class="caps"&gt;AMQP&lt;/span&gt;, XMPP, &lt;span class="caps"&gt;STOMP&lt;/span&gt;, ∅MQ, etc. But here&amp;#8217;s my best explanation of the pros and cons of each:&lt;/p&gt;


	&lt;p&gt;&lt;span class="caps"&gt;XMPP&lt;/span&gt;: Supports internet scale federation and addressing, and has a built-in presence service (the &amp;#8220;buddies list&amp;#8221;). Has only weak support for offline message delivery and complicated message routing.&lt;/p&gt;


	&lt;p&gt;&lt;span class="caps"&gt;AMQP&lt;/span&gt;: Excellent support for offline message delivery, and sophisticated message routing. &lt;span class="caps"&gt;AMQP&lt;/span&gt; is quite fast for messages &gt;= 100 bytes. The RabbitMQ server provides a distributed Erlang implementation, which &lt;em&gt;should&lt;/em&gt; make it especially suited to clustering. I&amp;#8217;m not sure what &lt;span class="caps"&gt;AMQP&lt;/span&gt; offers in the way of federation and global addressing.&lt;/p&gt;


	&lt;p&gt;∅MQ: Supports ~4 million messages per second per core, with extremely low latency. Excellent support for tiny messages, such as a stock ticker and price. Can interoperate with &lt;span class="caps"&gt;AMQP&lt;/span&gt;.&lt;/p&gt;


	&lt;p&gt;&lt;span class="caps"&gt;STOMP&lt;/span&gt;: I know nothing at all about this protocol, but it seems to be widely used in the Ruby community.&lt;/p&gt;


	&lt;p&gt;Basically, &lt;span class="caps"&gt;XMPP&lt;/span&gt; is optimized for people at the Internet scale, and &lt;span class="caps"&gt;AMQP&lt;/span&gt; is optimized for programs at a large organizational scale. ∅MQ was designed for use in high-performance stock-trading platforms, but to take full advantage of it, you&amp;#8217;d certainly need to use a language other than Ruby.&lt;/p&gt;


	&lt;p&gt;That was my massively uninformed summary of the major open source messaging queuing protocols. :-) If anybody has first-hand experience with any of the above, please feel free to correct my mistakes.&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2009 09:31:09 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:3431fa7f-998d-4fc3-bc1a-2c9ae1f0a9b2</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-624</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by Astro</title>
      <description>&lt;p&gt;Very interesting, but doesn&amp;#8217;t look thread-safe. Is it?&lt;/p&gt;


	&lt;p&gt;I tried to do a Shoes &lt;span class="caps"&gt;XMPP &lt;/span&gt;PubSub client before, but threading hassle killed it. Shoes and especially EventMachine are not made for threading.&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2009 08:50:23 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:6b48fcdd-bd74-4adb-8ed6-2487712ac2ec</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-623</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by Anon</title>
      <description>&lt;p&gt;What&amp;#8217;s so great about &lt;span class="caps"&gt;AMQP&lt;/span&gt;?&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2009 08:48:54 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:b22275ad-855f-4edd-8be5-458eb612fa88</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-622</link>
    </item>
    <item>
      <title>"Write a 32-line chat client using Ruby, AMQP &amp; EventMachine (and a GUI using Shoes)" by Anon</title>
      <description>&lt;p&gt;Who is Sarah?&lt;/p&gt;</description>
      <pubDate>Sat, 09 May 2009 05:45:09 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:c1343e78-8ccb-4ab2-bb6b-503285422df9</guid>
      <link>http://www.randomhacks.net/articles/2009/05/08/chat-client-ruby-amqp-eventmachine-shoes#comment-621</link>
    </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>
  </channel>
</rss>
