<?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 Ruby</title>
    <link>http://www.randomhacks.net/articles/tag/Ruby?tag=Ruby</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Technology and Other Fun Stuff</description>
    <item>
      <title>Screencast: Use Rails and RDF.rb to parse Best Buy product reviews</title>
      <description>&lt;p&gt;In the past few years, many companies have been embedding machine-readable metadata in their web pages. Among these is Best Buy, which &lt;a href="http://jay.beweep.com/category/rdfa/"&gt;provides extensive RDFa data&lt;/a&gt; describing their products, prices and user reviews.&lt;/p&gt;

&lt;p&gt;The following 20-minute screencast shows how to use Ruby 1.9.2, Rails 3.1rc1, &lt;a href="http://rdf.rubyforge.org/"&gt;RDF.rb&lt;/a&gt; and my &lt;a href="http://rdf-agraph.rubyforge.org/"&gt;rdf-agraph&lt;/a&gt; gem to compare user ratings of the iPad and various Android Honeycomb tablets.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2011/06/05/screencast-rails-rdf-agraph-product-reviews"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 05 Jun 2011 19:07:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f2fc22a5-16da-46a9-96a2-999264effcf1</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2011/06/05/screencast-rails-rdf-agraph-product-reviews</link>
      <category>Ruby</category>
      <category>Rails</category>
      <category>RDF</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/850</trackback:ping>
    </item>
    <item>
      <title>Heroku &amp;quot;Celadon Cedar&amp;quot; review</title>
      <description>&lt;p&gt;Heroku just released a new version of their hosting service for Ruby on Rails. It&amp;#8217;s called &lt;a href="http://blog.heroku.com/archives/2011/5/31/celadon_cedar/"&gt;Celadon Cedar&lt;/a&gt;, and it adds support for arbitrary background processes, Node.js servers and long-polling over HTTP. &lt;/p&gt;

&lt;p&gt;I just finished porting a large Rails 3.0 application to Heroku&amp;#8217;s Ceder stack from &lt;a href="http://www.opscode.com/chef/"&gt;Chef&lt;/a&gt;+&lt;a href="http://aws.amazon.com/ec2/"&gt;EC2&lt;/a&gt;, and I&amp;#8217;m deeply impressed. But there are still some rough edges, especially with regard to asset caching.&lt;/p&gt;

&lt;h3&gt;Procfiles are really cool&lt;/h3&gt;

&lt;p&gt;Previous versions of Heroku could only run two types of processes: Web servers and &lt;a href="http://rubydoc.info/gems/delayed_job/2.1.4/frames"&gt;delayed_job&lt;/a&gt; workers. If you needed to monitor a ZeroMQ queue or run a cron job every minute, you were out of luck. So even though I loved Heroku, about 2/3rds of my clients couldn&amp;#8217;t even consider using it.&lt;/p&gt;

&lt;p&gt;Celadon Cedar, however, allows you to create a &lt;code&gt;Procfile&lt;/code&gt; specifying a list of process types to run:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;web:    bundle exec rails server -p $PORT
worker: bundle exec rake jobs:work
clock:  bundle exec clockwork config/clock.rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you&amp;#8217;ve deployed your project, you can specify how many of each process you want:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;heroku scale web=3 worker=2 clock=1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even better, if you&amp;#8217;re running on a development machine, or if you want to deploy to a regular Linux server, you can use the &lt;a href="http://adam.heroku.com/past/2011/5/9/applying_the_unix_process_model_to_web_apps/"&gt;Foreman&lt;/a&gt; gem to launch the processes manually, or to generate init scripts:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;foreman start
foreman export upstart /etc/init -u username&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you&amp;#8217;re feeling more ambitious, you can also run &lt;a href="http://michaelvanrooijen.com/articles/2011/06/01-more-concurrency-on-a-single-heroku-dyno-with-the-new-celadon-cedar-stack/"&gt;Unicorn&lt;/a&gt; and &lt;a href="http://devcenter.heroku.com/articles/node-js"&gt;Node.js&lt;/a&gt; servers on Heroku.&lt;/p&gt;

&lt;h3&gt;Asset caching is even worse than before&lt;/h3&gt;

&lt;p&gt;Previous versions of Heroku had a built-in &lt;a href="http://www.varnish-cache.org/"&gt;Varnish cache&lt;/a&gt;, which would cache CSS, JavaScripts and images for 12 hours. The Varnish cache was automatically flushed on redeploy, so it gave you a nice performance boost for zero work.&lt;/p&gt;

&lt;p&gt;However, if you were running a high-performance site, you would generally want to run all your JavaScript and CSS through &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI Compressor&lt;/a&gt;, which  vastly improves your download times. Under the previous version of Heroku, this was annoying to set up: You had to either commit your compiled assets into &lt;code&gt;git&lt;/code&gt;, or deploy them to a CDN manually.&lt;/p&gt;

&lt;p&gt;The Celadon Cedar stack, unfortunately, doesn&amp;#8217;t make it any easier to set up YUI Compressor, and it removes the existing Varnish cache. In place of Varnish, Heroku &lt;a href="http://devcenter.heroku.com/articles/http-caching"&gt;encourages you to set up Rack::Cache with memcached as a storage backend&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You may want to consider adding the following line to your &lt;code&gt;config.ru&lt;/code&gt; file, right before the &lt;code&gt;run&lt;/code&gt; statement:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;use&lt;/span&gt; &lt;span class="constant"&gt;Rack&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Deflater&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Combined with Rack::Cache, this will give you back some of the functionality of Varnish. But it&amp;#8217;s a lot more work than you needed to do before, and the results aren&amp;#8217;t as good. Heroku made this decision deliberately, because Varnish prevented them for doing cool things with Node.js servers and long-polled HTTP connections. But it still represents a retreat from Heroku&amp;#8217;s famous ease of use.&lt;/p&gt;

&lt;p&gt;What Heroku&amp;#8217;s Cedar stack really needs is first-class support for Rack::Cache, Rack::Deflator, and the new &lt;a href="http://www.rubyinside.com/how-to-rails-3-1-coffeescript-howto-4695.html"&gt;Sprockets asset caching in Rails 3.1&lt;/a&gt;. Please, just allow me to add a couple of lines to my &lt;code&gt;Gemfile&lt;/code&gt; and have everything work automagically. Yeah, you&amp;#8217;ve spoiled me and made me lazy.&lt;/p&gt;

&lt;h3&gt;You&amp;#8217;ll have to upgrade to Ruby 1.9.2&lt;/h3&gt;

&lt;p&gt;According to the &lt;a href="http://devcenter.heroku.com/articles/cedar#stack_software_versions"&gt;official documentation&lt;/a&gt;, only Ruby 1.9.2 is supported under Celadon Cedar. This isn&amp;#8217;t entirely surprising—Rails 3.1 recommends Ruby 1.9.2 as well—but it may be a problem for some users.&lt;/p&gt;

&lt;p&gt;Fortunately, my client&amp;#8217;s application worked flawlessly under Ruby 1.9.2 with only a single change to the &lt;code&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Running a cron job once per minute is really easy, but it costs $71/month&lt;/h3&gt;

&lt;p&gt;One of Heroku&amp;#8217;s engineers explains how to run high-frequency cron jobs using &lt;a href="http://adam.heroku.com/past/2010/6/30/replace_cron_with_clockwork/"&gt;Clockwork&lt;/a&gt; and &lt;a href="http://rubydoc.info/gems/delayed_job/2.1.4/frames"&gt;delayed_job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basically, you add a couple of lines to your &lt;code&gt;Procfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;worker: bundle exec rake jobs:work
clock:  bundle exec clockwork config/clock.rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&amp;#8230;and you put something like the following in &lt;code&gt;config/clock.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="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;expand_path&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;../environment&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt;  &lt;span class="constant"&gt;__FILE__&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="comment"&gt;# Run our heartbeat once per minute.&lt;/span&gt;
&lt;span class="ident"&gt;every&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;minutes&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;myapp.heartbeat&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="constant"&gt;MyApp&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;delay&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;heartbeat&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a DelayedJob and hands it off to our worker process. According to the tutorial, you&amp;#8217;re supposed to do the actual work in a separate process, so as not to interfere with other events. This approach is elegant, but it&amp;#8217;s going to cost you $71/month for two &amp;#8220;dynos&amp;#8221;. Ouch.&lt;/p&gt;

&lt;h3&gt;Cedar is a great new stack, but it needs polishing&lt;/h3&gt;

&lt;p&gt;I&amp;#8217;m really impressed with Celadon Cedar. Heroku has vastly improved their support for complex applications with a lot of moving parts. But along the way, they&amp;#8217;ve made it slightly harder to deploy simple applications, and they still don&amp;#8217;t have a painless way to do asset caching. Of course, these minor drawbacks should improve dramatically once the Ruby community plays with Cedar for a few weeks.&lt;/p&gt;

&lt;p&gt;Many thanks to Heroku for a great new release! I&amp;#8217;ll be moving more applications over soon.&lt;/p&gt;

&lt;p&gt;Does anybody have any suggestions on how make better use of Cedar and Rails?&lt;/p&gt;</description>
      <pubDate>Fri, 03 Jun 2011 19:20:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:3df2e7b3-d7e1-4fe5-9468-ffa7596237a2</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2011/06/03/heroku-celadon-cedar-review</link>
      <category>Ruby</category>
      <category>Rails</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/849</trackback:ping>
    </item>
    <item>
      <title>The state of Ruby, RDF and Rails 3</title>
      <description>&lt;p&gt;Recently, I was investigating the state of RDF in the Ruby world.  Here are
some notes, in case anybody is curious.  I have used only a few of
these Ruby RDF libraries, so please feel free to add your own comments with
corrections and other alternatives.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s also some stuff about &lt;a href="http://rubyonrails.org/screencasts/rails3/active-relation-active-model"&gt;ActiveModel and ActiveRelation&lt;/a&gt; down at the end, for people who are interested in Rails 3.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2010/12/20/the-state-of-ruby-rdf-and-rails-3"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 20 Dec 2010 19:56:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:174f6eb6-ee3b-4c43-a665-b623fba65a63</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2010/12/20/the-state-of-ruby-rdf-and-rails-3</link>
      <category>Ruby</category>
      <category>RDF</category>
      <category>Rails</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/807</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>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>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>Ruby-style metaprogramming in JavaScript (plus a port of RSpec)</title>
      <description>&lt;p&gt;Programming in Ruby makes me happy.  It&amp;#8217;s a lovable language, with a
pleasantly quirky syntax and lots of expressive power.&lt;/p&gt;

&lt;p&gt;Programming in JavaScript, on the other hand, frustrates me to no end.
JavaScript &lt;i&gt;could&lt;/i&gt; be a reasonable language, but it has all sorts of
ugly corner cases, and it forces me to roll everything from scratch.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve been trying to make JavaScript a bit more like Ruby.  In particular, I
want to support Ruby-style metaprogramming in JavaScript.  This would make it possible to port over many advanced Ruby libraries.&lt;/p&gt;

&lt;p&gt;You can
check out the &lt;a href="http://www.randomhacks.net/svn/planetary/tags/0.1.0/spec_checker.html"&gt;interactive specification&lt;/a&gt;, or look at some examples
below.  If the &lt;a href="http://www.randomhacks.net/svn/planetary/tags/0.1.0/spec_checker.html"&gt;specification&lt;/a&gt; gives you any errors, please post them in the comment
thread, and let me know what browser you&amp;#8217;re running!&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2007/07/01/ruby-metaprogramming-javascript-rspec-bdd"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 01 Jul 2007 19:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:421e0747-a145-4fc3-b0f1-8a5323eaea01</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2007/07/01/ruby-metaprogramming-javascript-rspec-bdd</link>
      <category>Ruby</category>
      <category>JavaScript</category>
      <category>Macros</category>
      <category>Planetary</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/472</trackback:ping>
    </item>
    <item>
      <title>Some useful closures, in Ruby</title>
      <description>&lt;p&gt;Reginald Braithwaite has just posted &lt;a href="http://weblog.raganwald.com/2007/01/closures-and-higher-order-functions.html"&gt;a short introduction to closures in Ruby&lt;/a&gt;. Closures allow you to pass functions around your program, and build new functions from old ones.&lt;/p&gt;

&lt;p&gt;Programming languages that support closures include Perl, Ruby, Python (sorta), Lisp, Haskell, Dylan, Javascript and many others.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/Dylan_programming_language"&gt;Dylan programming language&lt;/a&gt; included four very useful functions built using closures: &lt;strong&gt;complement&lt;/strong&gt;, &lt;strong&gt;conjoin&lt;/strong&gt;, &lt;strong&gt;disjoin&lt;/strong&gt; and &lt;strong&gt;compose&lt;/strong&gt;.  The names are a bit obscure, but they can each be written in a few lines of Ruby.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s start with &lt;strong&gt;complement&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# Builds a function that returns true&lt;/span&gt;
&lt;span class="comment"&gt;# when 'f' returns false, and vice versa.&lt;/span&gt;
&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;complement&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;
  &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="keyword"&gt;not&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;)&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;We can use this to build the &amp;#8220;opposite&amp;#8221; of a function:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;is_even&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;n&lt;/span&gt; &lt;span class="punct"&gt;%&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;is_odd&lt;/span&gt;  &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;complement&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;is_even&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="ident"&gt;is_odd&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="comment"&gt;# true&lt;/span&gt;
&lt;span class="ident"&gt;is_odd&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="comment"&gt;# false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;compose&lt;/strong&gt; is another useful function:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# Builds a function which calls 'f' with&lt;/span&gt;
&lt;span class="comment"&gt;# the return value of 'g'.&lt;/span&gt;
&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;compose&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;g&lt;/span&gt;
  &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;g&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;))&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;We can use this to pass the output of one function to the input of another:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;mult2&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;add1&lt;/span&gt;  &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;+&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;mult2_add1&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;compose&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;add1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;mult2&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="ident"&gt;mult2_add1&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="comment"&gt;# 7&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;conjoin&lt;/strong&gt; function is a bit more complicated, but still very useful:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# Builds a function which returns true&lt;/span&gt;
&lt;span class="comment"&gt;# whenever _every_ function in 'predicates'&lt;/span&gt;
&lt;span class="comment"&gt;# returns true.&lt;/span&gt;
&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;conjoin&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;predicates&lt;/span&gt;
  &lt;span class="ident"&gt;base&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="ident"&gt;predicates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inject&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;base&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;built&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;pred&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;lambda&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;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;built&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="ident"&gt;pred&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;args&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;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use it to construct the logical &amp;#8220;and&amp;#8221; of a list of functions:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;is_number&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;kind_of?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Numeric&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;is_even_number&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;conjoin&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;is_number&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;is_even&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="ident"&gt;is_even_number&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="comment"&gt;# false&lt;/span&gt;
&lt;span class="ident"&gt;is_even_number&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;   &lt;span class="comment"&gt;# false&lt;/span&gt;
&lt;span class="ident"&gt;is_even_number&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;   &lt;span class="comment"&gt;# true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The opposite of &lt;strong&gt;conjoin&lt;/strong&gt; is &lt;strong&gt;disjoin&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# Builds a function which returns true&lt;/span&gt;
&lt;span class="comment"&gt;# whenever _any_ function in 'predicates'&lt;/span&gt;
&lt;span class="comment"&gt;# returns true.&lt;/span&gt;
&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;disjoin&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;predicates&lt;/span&gt;
  &lt;span class="ident"&gt;base&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="ident"&gt;predicates&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inject&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;base&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;built&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;pred&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;lambda&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;args&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;built&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;pred&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(*&lt;/span&gt;&lt;span class="ident"&gt;args&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;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This allows us to construct the logical &amp;#8220;or&amp;#8221; of a list of functions:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;is_string&lt;/span&gt;  &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;lambda&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;n&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;kind_of?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;String&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;is_string_or_number&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt;
  &lt;span class="ident"&gt;disjoin&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;is_string&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;is_number&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

&lt;span class="ident"&gt;is_string_or_number&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="comment"&gt;# true&lt;/span&gt;
&lt;span class="ident"&gt;is_string_or_number&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;   &lt;span class="comment"&gt;# true&lt;/span&gt;
&lt;span class="ident"&gt;is_string_or_number&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;call&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:a&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;  &lt;span class="comment"&gt;# false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These were four of the first closure-related functions I ever used, and they&amp;#8217;re still favorites today.&lt;/p&gt;

&lt;p&gt;Feel free to post versions in other languages below!&lt;/p&gt;</description>
      <pubDate>Thu, 01 Feb 2007 18:36:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:662731c8-031a-46e3-97bc-02668e09b272</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2007/02/01/some-useful-closures-in-ruby</link>
      <category>Ruby</category>
      <category>Hacks</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/245</trackback:ping>
    </item>
    <item>
      <title>13 Ways of Looking at a Ruby Symbol</title>
      <description>&lt;p&gt;New Ruby programmers often ask, &amp;#8220;What, exactly, &lt;em&gt;is&lt;/em&gt; a symbol? And how does
it differ from a string?&amp;#8221;  No one answer works for everybody, so&amp;#8211;with
&lt;a href="http://www.poets.org/viewmedia.php/prmMID/15746"&gt;apologies to Wallace Stevens&lt;/a&gt;&amp;#8211;here are 13 ways of looking at a
Ruby symbol.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 20 Jan 2007 03:20:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:46f3fe2b-e387-46d2-a682-8047b0fbf523</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol</link>
      <category>Ruby</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/194</trackback:ping>
    </item>
    <item>
      <title>Why Ruby is an acceptable LISP</title>
      <description>&lt;p&gt;Years ago, I looked at Ruby and decided to ignore it. Ruby wasn&amp;#8217;t as
popular as Python, and it wasn&amp;#8217;t as powerful as LISP. So why should I
bother?&lt;/p&gt;

&lt;p&gt;Of course, we could turn those criteria around.  What if Ruby were more
popular than &lt;em&gt;LISP&lt;/em&gt;, and more powerful than &lt;em&gt;Python&lt;/em&gt;?  Would that be enough
to make Ruby interesting?&lt;/p&gt;

&lt;p&gt;Before answering this question, we should decide what makes LISP so
powerful.  Paul Graham has &lt;a href="http://www.paulgraham.com/icad.html" title="Revenge of the Nerds"&gt;written eloquently&lt;/a&gt; about LISP&amp;#8217;s virtues.  But, for the sake of argument, I&amp;#8217;d like to boil them down to two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;LISP is a dense functional language.  &lt;/li&gt;
&lt;li&gt;LISP has programmatic macros.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As it turns out, Ruby compares well as a functional language, and it fakes
macros better than I&amp;#8217;d thought.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 03 Dec 2005 11:30:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:080ade06-c978-4e36-8847-44c22d1bc9b9</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp</link>
      <category>Ruby</category>
      <category>LISP</category>
      <category>Macros</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/77</trackback:ping>
    </item>
  </channel>
</rss>

