<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/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>OnRails.org: Category Rails Tips</title>
    <link>http://onrails.org/articles/category/rails-tips</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Ruby On Rails and related matters.</description>
    <item>
      <title>What are all the Rails Date Formats?</title>
      <description>&lt;p&gt;Ever forget what all the Rails defined &lt;code&gt;Date/DateTime/Time#strftime&lt;/code&gt; formats are?  Or forget what ones you added to the project yourself?&lt;/p&gt;


	&lt;p&gt;Ala &lt;code&gt;rake routes&lt;/code&gt; comes &lt;code&gt;rake date_formats&lt;/code&gt;:&lt;/p&gt;


&lt;script src="http://gist.github.com/1352.js"&gt;&lt;/script&gt;

Sample output from a Rails 2.1 app:
&lt;pre&gt;
Date
====
            db:'%Y-%m-%d'   2008-08-20
  long_ordinal:'&amp;#38;proc'      August 20th, 2008
          long:'%B %e, %Y'  August 20, 2008
        rfc822:'%e %b %Y'   20 Aug 2008
        number:'%Y%m%d'     20080820
         short:'%e %b'      20 Aug

DateTime
========
            db:'%Y-%m-%d'   2008-08-20 16:56:21
  long_ordinal:'&amp;#38;proc'      August 20th, 2008 16:56
          long:'%B %e, %Y'  August 20, 2008 16:56
        rfc822:'%e %b %Y'   Wed, 20 Aug 2008 16:56:21 -0600
        number:'%Y%m%d'     20080820165621
         short:'%e %b'      20 Aug 16:56

Time
====
            db:'%Y-%m-%d %H:%M:%S'         2008-08-20 16:56:21
  long_ordinal:'&amp;#38;proc'                     August 20th, 2008 16:56
          long:'%B %d, %Y %H:%M'           August 20, 2008 16:56
        rfc822:'%a, %d %b %Y %H:%M:%S %z'  Wed, 20 Aug 2008 16:56:21 -0600
         short:'%d %b %H:%M'               20 Aug 16:56
        number:'%Y%m%d%H%M%S'              20080820165621
          time:'%H:%M'                     16:56
&lt;/pre&gt;</description>
      <pubDate>Wed, 20 Aug 2008 22:54:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:2c07559a-8ec6-4f69-8d07-d657c497ffcd</guid>
      <author>Lee Marlow</author>
      <link>http://onrails.org/articles/2008/08/20/what-are-all-the-rails-date-formats</link>
      <category>Rails Tips</category>
    </item>
    <item>
      <title>Installing RMagick on Leopard (without MacPorts or Fink)</title>
      <description>&lt;p&gt;I&amp;#8217;ve recently upgraded to &lt;span class="caps"&gt;OS X 10&lt;/span&gt;.5 (Leopard), and all-in-all, I&amp;#8217;m pleased with the experience.  My biggest issue has been the default stacks behavior&amp;#8212;the icon changes to the last thing added to the stack, making visual identification unnecessarily cumbersome.  I worked around this annoyance (as outlined &lt;a href="http://www.ryanirelan.com/past/2007/10/31/leopard-stack"&gt;here&lt;/a&gt;) by changing the sort to name rather than date added, and adding a dummy folder named &amp;#8220;_1&amp;#8221; that will sort to the top.  For extra bonus points, I customized the icon of the dummy folder.  For some yet unknown reason, the most recently downloaded item still peeks through from time to time, but it&amp;#8217;s much better than before.&lt;/p&gt;


	&lt;p&gt;Maybe it&amp;#8217;s my Windows history showing through, but I went with the &amp;#8220;clean-sweep&amp;#8221; erase and install method.  For a non-developer, I&amp;#8217;d probably recommend the upgrade (and in fact I used that method for my Father-in-law&amp;#8217;s MacBook), but I had lots of custom bits scattered about my machine, and didn&amp;#8217;t want to be chasing any incompatibility gremlins.&lt;/p&gt;


	&lt;p&gt;So now, to get my development environment set up on the new machine&amp;#8230;  Leopard includes a fairly complete Rails stack out of the box, with a non-&lt;a href="http://hans.fugal.net/yodl/blosxom.cgi/mac/ruby-pack.html"&gt;broken&lt;/a&gt; Ruby, readline support, and most of the commonly used gems.  Read more &lt;a href="http://trac.macosforge.org/projects/ruby/wiki/WhatsNewInLeopard"&gt;here&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;MySQL was not included, but the latest installer (mysql-5.0.45-osx10.4-i686.dmg) for 10.4 from dev.mysql.com downloads worked (mostly) fine.  The Server and the StartupItem install and operate correctly.  The PrefPane installs, but does not appear to actually do &amp;#8230; anything.  I&amp;#8217;ll have to work on that, but I can live without it for now.  After a bit of manual hacking on my database dump file from Tiger (where I was running a 5.1.x beta of MySQL), all my databases are back in place.&lt;/p&gt;


	&lt;p&gt;One last piece that I needed for my Rails apps&amp;#8212;RMagick.  I know it&amp;#8217;s possible to install RMagick and its dependencies, um, &amp;#8220;autoRMagickally&amp;#8221; via a package management system like MacPorts or Fink, but I prefer not to.  For some background on why not, you can read this &lt;a href="http://hivelogic.com/narrative/articles/using_usr_local"&gt;article at hivelogic&lt;/a&gt;.  The last time I was rebuilding my laptop and desktop near the same time, I put together a shell script to automate the process of installing RMagick.  I got it back out and dusted off the cobwebs, and voila!  RMagick on Leopard.  (note: replace wget with &amp;#8220;curl -O&amp;#8221;, if you don&amp;#8217;t have wget installed on your machine)  Here&amp;#8217;s the code:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;install_rmagick.sh&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_sh "&gt;#!/bin/sh
wget http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz
tar xzvf freetype-2.3.5.tar.gz
cd freetype-2.3.5
./configure --prefix=/usr/local
make
sudo make install
cd ..

wget http://superb-west.dl.sourceforge.net/sourceforge/libpng/libpng-1.2.22.tar.bz2
tar jxvf libpng-1.2.22.tar.bz2
cd libpng-1.2.22
./configure --prefix=/usr/local
make
sudo make install
cd ..

wget ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
tar xzvf jpegsrc.v6b.tar.gz
cd jpeg-6b
ln -s `which glibtool` ./libtool
export MACOSX_DEPLOYMENT_TARGET=10.5
./configure --enable-shared --prefix=/usr/local
make
sudo make install
cd ..

wget ftp://ftp.remotesensing.org/libtiff/tiff-3.8.2.tar.gz
tar xzvf tiff-3.8.2.tar.gz
cd tiff-3.8.2
./configure --prefix=/usr/local
make
sudo make install
cd ..

wget http://jaist.dl.sourceforge.net/sourceforge/wvware/libwmf-0.2.8.4.tar.gz
tar xzvf libwmf-0.2.8.4.tar.gz
cd libwmf-0.2.8.4
make clean
./configure
make
sudo make install
cd ..

wget http://www.littlecms.com/lcms-1.17.tar.gz
tar xzvf lcms-1.17.tar.gz
cd lcms-1.17
make clean
./configure
make
sudo make install
cd ..

wget ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/gs860/ghostscript-8.60.tar.gz
tar zxvf ghostscript-8.60.tar.gz
cd ghostscript-8.60/
./configure  --prefix=/usr/local
make
sudo make install
cd ..

wget ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/current/ghostscript-fonts-std-8.11.tar.gz
tar zxvf ghostscript-fonts-std-8.11.tar.gz
sudo mv fonts /usr/local/share/ghostscript

wget http://imagemagick.site2nd.org/imagemagick/ImageMagick-6.3.5-9.tar.gz
tar xzvf ImageMagick-6.3.5-9.tar.gz
cd ImageMagick-6.3.5
export CPPFLAGS=-I/usr/local/include
export LDFLAGS=-L/usr/local/lib
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install
cd ..

sudo gem install RMagick&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <pubDate>Sat, 03 Nov 2007 00:07:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:58aaed88-7522-4004-8863-8f57e819423c</guid>
      <author>Solomon White</author>
      <link>http://onrails.org/articles/2007/11/03/installing-rmagick-on-leopard-without-macports-or-fink</link>
      <category>Rails Tips</category>
      <category>Ruby On Rails</category>
      <category>Rails</category>
      <category>rmagick</category>
      <category>leopard</category>
    </item>
    <item>
      <title>Monitoring Rails Performance with Munin and a Mongrel</title>
      <description>&lt;p&gt;Rails makes things easy on developers&amp;#8212;maybe &lt;strong&gt;too&lt;/strong&gt; easy.  It&amp;#8217;s not uncommon to reference an association while iterating over a collection of objects, resulting in a performance-devouring N+1 queries being executed.  Of course, this is easily fixed with eager loading of the association, but you get my point.  Rails can be a big gun, and it&amp;#8217;s easy to blow off your foot.&lt;/p&gt;


	&lt;p&gt;Once your application is moved into production, it becomes important to keep an eye on performance over time in order to get a feel for trends and plan capacity intelligently.  Numerous Rails &lt;a href="http://rails-analyzer.rubyforge.org/"&gt;performance&lt;/a&gt; &lt;a href="http://cfis.savagexi.com/articles/2007/07/10/how-to-profile-your-rails-application"&gt;measuring&lt;/a&gt; &lt;a href="http://railsbench.rubyforge.org/"&gt;tools&lt;/a&gt; exist, but I find that it helps to correlate performance of your application with simultaneous lower-level performance metrics of your system (CPU and Memory usage, Load Average, etc).  Besides that, hey!  Pretty Graphs!  Enter &lt;a href="http://munin.projects.linpro.no/"&gt;munin&lt;/a&gt;, an open-source, extensible monitoring tool with a number of out-of-the-box plugins that are useful to SysAdmin type folks.  There&amp;#8217;s a pretty decent (though brief) &lt;a href="http://www.howtoforge.com/server_monitoring_monit_munin"&gt;writeup on howtoforge&lt;/a&gt; that explains a bit more about how to go about getting your own munin.  Go ahead and check it out&amp;#8212;I&amp;#8217;ll chill here until you&amp;#8217;re back.&lt;/p&gt;


&lt;center&gt;...&lt;/center&gt;

	&lt;p&gt;That wasn&amp;#8217;t too bad, huh?  Okay, so at this point you should have a working munin instance.  It will take a few minutes of data collection before your graphs look like anything, but going forward, you&amp;#8217;ll have historical graphs of several key metrics for managing your server(s).  It&amp;#8217;s outside the scope of this article, but you can also set up munin to monitor multiple servers on your network, and/or alert you when critical threshold levels are passed.&lt;/p&gt;


	&lt;p&gt;Well, knowing &lt;span class="caps"&gt;CPU&lt;/span&gt; usage is great, but it would also be nice to have some idea what the average user experience is like on your site.  Does your Rails app perform differently at different times of day?  How long does it typically take for your app to render a page?  Is the majority of the time spent in the database, or rendering?  Is the response time about the same between production deployment versions?  I wanted answers to these questions too, so I came up with a small ruby program that watches your Rails log in realtime, and when munin asks, provides summary information about the performance of your application.  Below, I&amp;#8217;ll go over the code section by section; for those who want it now, scroll to the bottom of the article to find the download link.&lt;/p&gt;


	&lt;p&gt;The basis of the solution is a Ruby array:  we stuff values into it, then compute the average of all the values and clear the array each time munin pings us.  Every time a value is added, we also check it against the maximum already seen, so we can report the maximum response time in addition to the average.  We keep three of these objects around, one each for DB, Rendering, and Total response times.  There is also a mode that lets you look at the current value without consuming it&amp;#8212;useful for peeking inside without affecting the data that munin will ultimately see.  Here&amp;#8217;s the class that implements this functionality:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;accumulator&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Accumulator&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;
    &lt;span class="attribute"&gt;@values&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;
    &lt;span class="attribute"&gt;@max&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&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;add&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@values&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;
    &lt;span class="attribute"&gt;@max&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt; &lt;span class="punct"&gt;&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@max&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;average&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;read_only&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;return_value&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="attribute"&gt;@values&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;0&lt;/span&gt;
      &lt;span class="constant"&gt;nil&lt;/span&gt;
    &lt;span class="keyword"&gt;else&lt;/span&gt;
      &lt;span class="attribute"&gt;@values&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="number"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;sum&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;sum&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt; @values.length
    end
    @values = Array.new() unless read_only

    return_value
  end

  def max(read_only=false)
    return_value = @max
    @max = 0 unless read_only
    return_value
  end
end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;In the next section of the code, we build our accumulators, and begin tailing the logfile to extract performance numbers.  This requires the &lt;a href="http://file-tail.rubyforge.org/"&gt;file-tail gem&lt;/a&gt;, available from rubyforge.  Note that in my setup, this file resides in a subdirectory under &lt;code&gt;lib&lt;/code&gt; in &lt;code&gt;RAILS_ROOT&lt;/code&gt;.  If you choose to place this file elsewhere, you&amp;#8217;ll have to adjust the path to the logfile accordingly.  Another thing to note:  in our environment, the load balancer continually pings a &amp;#8220;heartbeat&amp;#8221; action on each node to make sure it is still responsive.  As we will be hitting this action repeatedly, it is engineered to be as lightweight as possible.  Therefore, any numbers from it are pretty meaningless to our overall statistics, so we don&amp;#8217;t want to include them.  To keep these numbers from skewing our results, we define an &lt;code&gt;IGNORE_PATTERNS&lt;/code&gt; regexp (earlier in the code).  If the request matches a pattern we want to ignore, its statistics are not collected.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;tail&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="constant"&gt;LOGFILE&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="ident"&gt;join&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="ident"&gt;dirname&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="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;..&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;..&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;log&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;&lt;span class="expr"&gt;#{RAILS_ENV}&lt;/span&gt;.log&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
&lt;span class="global"&gt;$response_data&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:total&lt;/span&gt;     &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Accumulator&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(),&lt;/span&gt;
                   &lt;span class="symbol"&gt;:rendering&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Accumulator&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(),&lt;/span&gt;
                   &lt;span class="symbol"&gt;:db&lt;/span&gt;        &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;Accumulator&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt; &lt;span class="punct"&gt;}&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;abort_on_exception&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
&lt;span class="ident"&gt;logtail&lt;/span&gt; &lt;span class="punct"&gt;=&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="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Tail&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Logfile&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tail&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;LOGFILE&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;line&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;line&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^Completed in &lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
      &lt;span class="ident"&gt;parts&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;line&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\s&lt;/span&gt;+&lt;span class="escape"&gt;\|\s&lt;/span&gt;+&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;
      &lt;span class="ident"&gt;resp&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;parts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pop&lt;/span&gt;
      &lt;span class="ident"&gt;requested_url&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;resp&lt;/span&gt;&lt;span class="punct"&gt;[/&lt;/span&gt;&lt;span class="regex"&gt;http:&lt;span class="escape"&gt;\/\/&lt;/span&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;next&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;requested_url&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="constant"&gt;IGNORE_PATTERNS&lt;/span&gt;

      &lt;span class="ident"&gt;parts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&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;part&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;part&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub!&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;Completed in&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;total&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
        &lt;span class="ident"&gt;type&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;time&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;pct&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;part&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\s&lt;/span&gt;+&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;
        &lt;span class="ident"&gt;type&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;type&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub&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="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;downcase&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_sym&lt;/span&gt;

        &lt;span class="global"&gt;$response_data&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;type&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;add&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;time&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_f&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;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;So now we have a thread busy gathering our data&amp;#8212;how can we expose the data to a munin plugin?  There are multiple ways to do this, but I chose to use a small &lt;span class="caps"&gt;HTTP&lt;/span&gt; server listening to requests from the local machine only.  We could build such a thing in Rails, but we really don&amp;#8217;t need about 90% of the features Rails has to offer.  Since we&amp;#8217;re running Rails as a Mongrel cluster, we already have a perfect tool at our disposal for writing small &lt;span class="caps"&gt;HTTP&lt;/span&gt; request handlers in Ruby:  Mongrel.  Here are a couple of pages about how to get started &lt;a href="http://www.urbanhonking.com/ideasfordozens/archives/2007/05/my_first_mongre.html"&gt;writing&lt;/a&gt; &lt;a href="http://adam.blogs.bitscribe.net/2007/05/06/howto-custom-mongrel-handlers/"&gt;Mongrel&lt;/a&gt; &lt;a href="http://errtheblog.com/post/8444"&gt;handlers&lt;/a&gt;&amp;#8212;it&amp;#8217;s pretty straightforward.  Here&amp;#8217;s our handler:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;mongrel handler&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ResponseTimeHandler&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;Mongrel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;HttpHandler&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@method&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;method&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;process&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;request&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;response&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="number"&gt;200&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;head&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;out&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;debug&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Mongrel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;HttpRequest&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;query_parse&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;request&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;QUERY_STRING&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]).&lt;/span&gt;&lt;span class="ident"&gt;has_key?&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;debug&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;head&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Content-Type&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&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;text/plain&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;output&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$response_data&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;map&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;k&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;value&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@method&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;debug&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;formatted&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;value&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;U&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;sprintf&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;%.5f&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;value&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;&lt;span class="expr"&gt;#{k}&lt;/span&gt;.value &lt;span class="expr"&gt;#{formatted}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&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;output&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&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;out&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;write&lt;/span&gt; &lt;span class="ident"&gt;output&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="ident"&gt;h&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Mongrel&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;HttpServer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;127.0.0.1&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="constant"&gt;PORT&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;register&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/avg_response_time&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="constant"&gt;ResponseTimeHandler&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:average&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;register&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/max_response_time&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="constant"&gt;ResponseTimeHandler&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:max&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;run&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The handler is generic so that it can call an arbitrary method on our collection of data arrays, so we can set up one &lt;span class="caps"&gt;URI&lt;/span&gt; for the average, and one for the maximum.  With this in place, we can write a simple munin plugin script that uses &lt;code&gt;Net::HTTP&lt;/code&gt; to query our Mongrel to get at the performance data.&lt;/p&gt;


	&lt;p&gt;Basically, a munin plugin has two usage scenarios.  When called with the single argument &amp;#8220;config&amp;#8221;, it should output information about itself in a format that Munin understands.  This includes how to label the chart, scaling information, how many series will be included on the chart, etc.  When called with no arguments, the plugin should output the current values of each series.  For more information on writing your own munin plugin, start with the &lt;a href="http://munin.projects.linpro.no/wiki/HowToWritePlugins"&gt;HowToWritePlugins munin wiki page&lt;/a&gt;.  And now, our plugin script:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;div class="codetitle"&gt;rails_response_time&lt;/div&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class="comment"&gt;# munin plugin to render rails response time graphs&lt;/span&gt;
&lt;span class="comment"&gt;# link to /etc/munin/plugins/avg_response_time and /etc/munin/plugins/max_response_time&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;open-uri&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="constant"&gt;PORT&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;PORT&lt;/span&gt;&lt;span class="punct"&gt;']&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;8888&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;config&lt;/span&gt;
  &lt;span class="ident"&gt;title&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="ident"&gt;basename&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="global"&gt;$0&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;_&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt;&lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;s&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;capitalize&lt;/span&gt; &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt; &lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
  &lt;span class="ident"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;=&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="constant"&gt;__END_CONFIG__&lt;/span&gt;&lt;span class="string"&gt;
graph_title &lt;span class="expr"&gt;#{title}&lt;/span&gt;
graph_vlabel response time
graph_category rails
total.label total
rendering.label rendering
db.label db
&lt;/span&gt;&lt;span class="constant"&gt;__END_CONFIG__&lt;/span&gt;
  &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;config&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;get_data&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;read_only&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;qs&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;read_only&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;?debug&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;open&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;http://127.0.0.1:&lt;span class="expr"&gt;#{PORT}&lt;/span&gt;/&lt;span class="expr"&gt;#{File.basename($0)}#{qs}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;).&lt;/span&gt;&lt;span class="ident"&gt;read&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;case&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;first&lt;/span&gt;
&lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;config&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;config&lt;/span&gt;
&lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;debug&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="ident"&gt;get_data&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="keyword"&gt;else&lt;/span&gt;
  &lt;span class="ident"&gt;get_data&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The script will examine the name with which you linked it in to the munin plugins directory to determine which &lt;span class="caps"&gt;URI&lt;/span&gt; to query.  I have also added a debug mode that will show you the current values, so you&amp;#8217;re not consuming any data that munin needs to see for an accurate graph.  The final piece is a small &lt;a href="http://daemons.rubyforge.org/"&gt;Daemons&lt;/a&gt; wrapper script to control the main log-tailing process, and you should be set.  Make sure to restart munin-node so it will notice the new plugins, and after a while, you&amp;#8217;ll see something like this:&lt;/p&gt;


	&lt;p&gt;&lt;img src="/files/avg_response_time.png" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;It is worth noting that the numbers from the Rails log might not be 100% accurate, and this won&amp;#8217;t replace the results that you can get from seriously profiling your application.  Also, the information you are getting is a bit generic&amp;#8212;all actions are lumped together, so there is not a lot of information about the cause of the performance problem.  But, for insight into your production application performance, this setup should at least give you some indications about how well your baby is playing in the interwebs.&lt;/p&gt;


	&lt;p&gt;Download &lt;a href="/files/rails_log_monitor.rb"&gt;rails_log_monitor.rb&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Download &lt;a href="/files/rails_response_time"&gt;rails_response_time&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 31 Aug 2007 18:29:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:e71c0dbd-1196-4b9f-8faf-ce5e92b1bee0</guid>
      <author>Solomon White</author>
      <link>http://onrails.org/articles/2007/08/31/monitoring-rails-performance-with-munin-and-a-mongrel</link>
      <category>Ruby On Rails</category>
      <category>Rails Tips</category>
      <category>mongrel</category>
      <category>munin</category>
      <category>performance</category>
      <category>monitoring</category>
      <category>Rails</category>
      <enclosure url="http://onrails.org/files/rails_log_monitor.rb" length="2411" type="application/octet-stream"/>
    </item>
    <item>
      <title>Bloated RailsConf Presentation Downloader</title>
      <description>&lt;p&gt;I&amp;#8217;ve updated my downloader from &lt;a href="/articles/2007/05/21/download-railsconf-2007-presentations"&gt;earlier&lt;/a&gt; to include all sorts of fancy options.  It no longer requires wget, it just uses open-uri.  It can give the files a fancy name.  It can be told where to download the files to.  It will skip files that won&amp;#8217;t download for some reason.  It will even butter your toast if you can find the correct command line switch.&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s about 3 times bigger than the previous one.  But maybe you can learn a little more about &lt;a href="http://www.ruby-doc.org/core/classes/OptionParser.html"&gt;optparse&lt;/a&gt;, &lt;a href="http://code.whytheluckystiff.net/hpricot/"&gt;hpricot&lt;/a&gt;, file handling, and error handling along the way.&lt;/p&gt;


	&lt;p&gt;Here it is:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;#!/usr/bin/env ruby&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;optparse&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="constant"&gt;OPTIONS&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:Verbose&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
            &lt;span class="symbol"&gt;:Force&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
            &lt;span class="symbol"&gt;:DownloadDir&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;.&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt;
            &lt;span class="symbol"&gt;:DescriptiveFilenames&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
          &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="constant"&gt;OptionParser&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="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
  &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;banner&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;Usage: &lt;span class="expr"&gt;#{$0}&lt;/span&gt; [options]&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;

  &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;on&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;-v&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;--[no-]verbose&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Run verbosely, default &lt;span class="expr"&gt;#{OPTIONS[:Verbose]}&lt;/span&gt;&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="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;verbose&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:Verbose&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;verbose&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;on&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;-f&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;--[no-]force&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Force downloads, default &lt;span class="expr"&gt;#{OPTIONS[:Force]}&lt;/span&gt;&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="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;force&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:Force&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;force&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;on&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;-d&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;--[no-]descriptive&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Use long descriptive filenames, default &lt;span class="expr"&gt;#{OPTIONS[:DescriptiveFilenames]}&lt;/span&gt;&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="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;long&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:DescriptiveFilenames&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;long&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;on&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;-p&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;--path PATH&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Path to download to, default &lt;span class="expr"&gt;#{OPTIONS[:DownloadDir]}&lt;/span&gt;&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="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;path&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:DownloadDir&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;path&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;on_tail&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;-h&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;--help&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Print help message&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="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;help&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;opts&lt;/span&gt;
    &lt;span class="ident"&gt;exit&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;parse!&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;rubygems&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;hpricot&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;open-uri&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;fileutils&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="constant"&gt;BASE_URL&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;http://www.web2expo.com&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;log&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="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;str&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:Verbose&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;download&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;href&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;filename&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="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{BASE_URL}#{URI.escape(href)}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="ident"&gt;download_file&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="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:DownloadDir&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="ident"&gt;filename&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:Force&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;||&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="ident"&gt;exists?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;download_file&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;log&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;downloading &lt;span class="expr"&gt;#{File.basename(href)}&lt;/span&gt;...&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;begin&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;open&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;download_file&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;w&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="punct"&gt;{&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;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;write&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;open&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;read&lt;/span&gt;&lt;span class="punct"&gt;)}&lt;/span&gt;
      &lt;span class="ident"&gt;log&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\t&lt;/span&gt;saved as &lt;span class="expr"&gt;#{download_file}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;Object&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="constant"&gt;FileUtils&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;rm&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;download_file&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="global"&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;ERROR downloading &lt;span class="expr"&gt;#{url}&lt;/span&gt;: &lt;span class="expr"&gt;#{e.message}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;else&lt;/span&gt;
    &lt;span class="ident"&gt;log&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;skipping &lt;span class="expr"&gt;#{File.basename(href)}&lt;/span&gt;... already downloaded as &lt;span class="expr"&gt;#{download_file}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="constant"&gt;FileUtils&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;mkdir_p&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:DownloadDir&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
&lt;span class="ident"&gt;h&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Hpricot&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;open&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{BASE_URL}&lt;/span&gt;/pub/w/51/presentations.html&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;))&lt;/span&gt;
&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;search&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div.presentation&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;each&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;presentation_node&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
  &lt;span class="ident"&gt;href&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;presentation_node&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;at&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;a[@href^=&amp;quot;/presentations/rails2007/&amp;quot;]&lt;/span&gt;&lt;span class="punct"&gt;')[&lt;/span&gt;&lt;span class="symbol"&gt;:href&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="constant"&gt;OPTIONS&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:DescriptiveFilenames&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;presentation_node&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;at&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;b a&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;inner_text&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;strip&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;presentation_node&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;inner_text&lt;/span&gt;
    &lt;span class="ident"&gt;speaker&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="regex"&gt;Speaker&lt;span class="escape"&gt;\(&lt;/span&gt;s&lt;span class="escape"&gt;\)&lt;/span&gt;:&lt;span class="escape"&gt;\s&lt;/span&gt;+(.*)&lt;span class="escape"&gt;\s&lt;/span&gt;*$&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;date&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Date&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;parse&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="regex"&gt;Presentation Date:&lt;span class="escape"&gt;\s&lt;/span&gt;+(.*)&lt;span class="escape"&gt;\s&lt;/span&gt;*$&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;filename&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;speaker&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;date&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;name&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="ident"&gt;basename&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;href&lt;/span&gt;&lt;span class="punct"&gt;)].&lt;/span&gt;&lt;span class="ident"&gt;compact&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;s&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;s&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="ident"&gt;strip&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;gsub&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;[^&lt;span class="escape"&gt;\w\.&lt;/span&gt;]&lt;/span&gt;&lt;span class="punct"&gt;/,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;_&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;squeeze&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;_&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;-&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
  &lt;span class="keyword"&gt;else&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;basename&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;href&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="ident"&gt;download&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;href&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;filename&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;</description>
      <pubDate>Mon, 21 May 2007 22:49:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:cd3369f0-f3fb-4269-ac9f-e2b6c5fbe9ec</guid>
      <author>Lee Marlow</author>
      <link>http://onrails.org/articles/2007/05/21/bloated-railsconf-presentation-downloader</link>
      <category>Ruby On Rails</category>
      <category>Rails Tips</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>Download RailsConf 2007 Presentations</title>
      <description>&lt;p&gt;&lt;strong&gt;Updated: &lt;a href="/articles/2007/05/21/bloated-railsconf-presentation-downloader"&gt;Now more bloated!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

Run this to get the &lt;a href="http://www.web2expo.com/pub/w/51/presentations.html"&gt;RailsConf 2007 presentations&lt;/a&gt;:

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;&lt;span class="comment"&gt;#!/usr/bin/env ruby&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;rubygems&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;hpricot&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;open-uri&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="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;http://www.web2expo.com&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;h&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Hpricot&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;open&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{base}&lt;/span&gt;/pub/w/51/presentations.html&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;))&lt;/span&gt;

&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;search&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;div .presentation &amp;gt; a[@href^=&amp;quot;/presentations/rails2007/&amp;quot;]&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;each&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;a&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="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{base}#{a[:href]}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;if&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;exists?&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="ident"&gt;basename&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;puts&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;skipping &lt;span class="expr"&gt;#{url}&lt;/span&gt;... already downloaded&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;else&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;downloading &lt;span class="expr"&gt;#{url}&lt;/span&gt;...&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
    `&lt;span class="ident"&gt;wget&lt;/span&gt; &lt;span class="punct"&gt;--&lt;/span&gt;&lt;span class="ident"&gt;quiet&lt;/span&gt; &lt;span class="comment"&gt;#{url}`&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

I might clean it up more later to name the files better and not use wget, but this was quick and easy... not to mention a way to use everyone's favorite parsing tool: &lt;a href="http://code.whytheluckystiff.net/hpricot/"&gt;Hpricot&lt;/a&gt;.</description>
      <pubDate>Mon, 21 May 2007 18:27:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:fc29fe70-707b-484a-9ba7-eda8f414a74e</guid>
      <author>Lee Marlow</author>
      <link>http://onrails.org/articles/2007/05/21/download-railsconf-2007-presentations</link>
      <category>Ruby</category>
      <category>Rails Tips</category>
      <category>Ruby On Rails</category>
    </item>
    <item>
      <title>TextMate filetype detection for script/runner Rails scripts</title>
      <description>&lt;p&gt;So you&amp;#8217;re building some righteous automation for your killer web 2.0 app, placing scripts in &lt;code&gt;RAILS_ROOT/script&lt;/code&gt; that you can call from cron for nightly maintenance, etc.  To bootstrap your rails environment, you decide to use the shebang feature of script/runner, available since &lt;a href="http://dev.rubyonrails.org/changeset/5189"&gt;changeset 5189&lt;/a&gt;.  When you start to edit the script in TextMate (you &lt;strong&gt;are&lt;/strong&gt; using TextMate, aren&amp;#8217;t you?) there is no syntax highlighting to be found!  It&amp;#8217;s all plain text with no colors, and none of your ever-so-helpful keyboard macros work!  Frightful.  Well, take a deep breath, because together, we&amp;#8217;re going to get the filetype detection magic working for you.&lt;/p&gt;


	&lt;p&gt;Before we get started, it&amp;#8217;s helpful to know how filetype detection works.  TextMate does a couple of different types of filetype detection&amp;#8212;the first is based off of the extension, so if you named your script with a &lt;code&gt;.rb&lt;/code&gt; extension, you are probably wondering what in the world I&amp;#8217;m rambling about.  Dude.  It just works.&lt;/p&gt;


	&lt;p&gt;However, if you followed the rails convention for scripts, and did not use an extension with your filename, keep reading.  The second type of detection works by scanning the so called &amp;#8220;shebang&amp;#8221; line at the top of the script which tells the shell (and in this case TextMate) which interpreter to use to evaluate your script&amp;#8212;this is how we will tell TextMate that script/runner really means ruby.&lt;/p&gt;


	&lt;p&gt;First of all, you&amp;#8217;ll need to fire up the Bundle Editor and select &amp;#8220;Languages&amp;#8221; from the drop-down filter.  Expand the &amp;#8220;Rails&amp;#8221; node, and then select the &amp;#8220;Ruby on Rails&amp;#8221; language.  On the right side, you should see the definition being used by TextMate to detect the Ruby on Rails scope.  If you have not modified your bundle, you&amp;#8217;ll probably see that it is using a &lt;code&gt;fileTypes&lt;/code&gt; to look for &lt;code&gt;.rxml&lt;/code&gt; files.  This is where we want to insert the following line:&lt;/p&gt;


	&lt;p&gt;&lt;code&gt;    firstLineMatch = '^#!.*(script/runner)';&lt;/code&gt;&lt;/p&gt;


	&lt;p&gt;Here&amp;#8217;s a screenshot of what it should look like when you are done:
&lt;img src="http://onrails.org/files/BundleEditor-ROR.gif" alt="" /&gt;&lt;/p&gt;


	&lt;p&gt;Now go back to your script and enjoy all the colorized, scope-aware editing goodness that is TextMate!&lt;/p&gt;</description>
      <pubDate>Fri, 20 Apr 2007 14:54:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:5c2e6ed5-7d4d-469e-ba06-b9b34d68c913</guid>
      <author>Solomon White</author>
      <link>http://onrails.org/articles/2007/04/20/textmate-filetype-detection-for-script-runner-rails-scripts</link>
      <category>Rails Tips</category>
      <category>Ruby On Rails</category>
      <category>Rails</category>
      <category>script/runner</category>
      <category>textmate</category>
    </item>
    <item>
      <title>Rake Command Completion Using Rake</title>
      <description>&lt;p&gt;This is an update of a &lt;a href="http://onrails.org/articles/2006/08/30/namespaces-and-rake-command-completion"&gt;previous&lt;/a&gt; post.&lt;/p&gt;
&lt;p&gt;I just watched &lt;a href="http://onestepback.org/"&gt;Jim Weirich's&lt;/a&gt; talk on &lt;a href="http://rake.rubyforge.org/"&gt;Rake&lt;/a&gt; at the &lt;a href="http://pragmaticstudio.com/therailsedge/denver.html"&gt;Rails Edge Studio&lt;/a&gt; and decided to update the command completion script to use Rake itself for caching the tasks.  &lt;a href="http://errtheblog.com/post/33"&gt;Err the Blog&lt;/a&gt; did an update on my script last month to include caching for a nice speedup, so I took that and made sure the cache is regenerated when the Rakefile or any rake tasks found in your rails app have been updated.  I added this with just some straight ruby code, but Jim's talk made me realize that this type of thing is exactly is what rake is for.  So, here is the updated version:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;  &lt;span class="comment"&gt;#!/usr/bin/env ruby&lt;/span&gt;

  &lt;span class="comment"&gt;# Complete rake tasks script for bash&lt;/span&gt;
  &lt;span class="comment"&gt;# Save it somewhere and then add&lt;/span&gt;
  &lt;span class="comment"&gt;# complete -C path/to/script -o default rake&lt;/span&gt;
  &lt;span class="comment"&gt;# to your ~/.bashrc&lt;/span&gt;
  &lt;span class="comment"&gt;# Nicholas Seckar &amp;lt;nseckar@gmail.com&amp;gt;&lt;/span&gt;
  &lt;span class="comment"&gt;# Saimon Moore &amp;lt;saimon@webtypes.com&amp;gt;&lt;/span&gt;
  &lt;span class="comment"&gt;# http://www.webtypes.com/2006/03/31/rake-completion-script-that-handles-namespaces&lt;/span&gt;
  &lt;span class="comment"&gt;# http://errtheblog.com/post/33&lt;/span&gt;

  &lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;unless&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;file?&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="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pwd&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Rakefile&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;rubygems&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;rake&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
  &lt;span class="constant"&gt;DOTCACHE&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="ident"&gt;join&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="ident"&gt;expand_path&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;~&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;.rake_task_cache&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;Dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pwd&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;hash&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="constant"&gt;RAKE_FILES&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;FileList&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="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Rakefile&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;lib/tasks/**/*.rake&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;vendor/plugins/*/tasks/**/*.rake&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt;
  &lt;span class="ident"&gt;file&lt;/span&gt; &lt;span class="constant"&gt;DOTCACHE&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;RAKE_FILES&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;tasks&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; `&lt;span class="ident"&gt;rake&lt;/span&gt; &lt;span class="punct"&gt;--&lt;/span&gt;&lt;span class="ident"&gt;silent&lt;/span&gt; &lt;span class="punct"&gt;--&lt;/span&gt;&lt;span class="ident"&gt;tasks`&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&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;map&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;line&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;line&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&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="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;fileutils&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="ident"&gt;dirname&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="ident"&gt;dirname&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;DOTCACHE&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="constant"&gt;FileUtils&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;mkdir_p&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;dirname&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;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;exists?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;dirname&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="ident"&gt;open&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;DOTCACHE&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;w&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt; &lt;span class="punct"&gt;{&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;f&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Task&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="constant"&gt;DOTCACHE&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;invoke&lt;/span&gt;
  &lt;span class="ident"&gt;tasks&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="ident"&gt;read&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;DOTCACHE&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

  &lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;&lt;span class="escape"&gt;\b&lt;/span&gt;rake&lt;span class="escape"&gt;\b&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;COMP_LINE&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&lt;/span&gt;

  &lt;span class="ident"&gt;after_match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$'&lt;/span&gt;
  &lt;span class="ident"&gt;task_match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;after_match&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;after_match&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 class="escape"&gt;\s&lt;/span&gt;$&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;after_match&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;last&lt;/span&gt;
  &lt;span class="ident"&gt;tasks&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&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 class="expr"&gt;#{Regexp.escape task_match}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="ident"&gt;t&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;task_match&lt;/span&gt;

  &lt;span class="comment"&gt;# handle namespaces&lt;/span&gt;
  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;task_match&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 class="escape"&gt;\w&lt;/span&gt;:]+:)&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
    &lt;span class="ident"&gt;upto_last_colon&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;
    &lt;span class="ident"&gt;after_match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$'&lt;/span&gt;
    &lt;span class="ident"&gt;tasks&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;t&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 class="expr"&gt;#{Regexp.escape upto_last_colon}&lt;/span&gt;([-&lt;span class="escape"&gt;\w&lt;/span&gt;:]+)$&lt;/span&gt;&lt;span class="punct"&gt;/)&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;&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="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;
  &lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I fixed the bug that &lt;a href="http://onrails.org/articles/2006/11/17/rake-command-completion-using-rake#comment-227"&gt;janfri&lt;/a&gt; pointed out.  The bug caused the first task to be missed.  I also changed it so it won't abort if rake isn't the first command on the command line.  This will allow stringing multiple commands together.  For instance:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;rake db:migrate VERSION=0 &amp;amp;&amp;amp; rake db:migrate&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="http://onrails.org/files/rake-completion.rb"&gt;Enjoy&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 17 Nov 2006 15:57:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:0f1eb26e-8f30-4c04-91f9-04e10a6e7cfb</guid>
      <author>Lee Marlow</author>
      <link>http://onrails.org/articles/2006/11/17/rake-command-completion-using-rake</link>
      <category>Ruby</category>
      <category>Rails Tips</category>
    </item>
    <item>
      <title>Namespaces and Rake Command Completion</title>
      <description>&lt;strong&gt;Update:&lt;/strong&gt; &lt;a href="http://onrails.org/articles/2006/11/17/rake-command-completion-using-rake"&gt;Now using rake and caching&lt;/a&gt;

&lt;p&gt;I got some basic rake command line completion working today using Jon Baer&amp;#8217;s &lt;a href="http://weblog.rubyonrails.org/2006/3/9/fast-rake-task-completion-for-zsh#comment-2361"&gt;comment&lt;/a&gt;.  Very simple, very easy:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;&lt;notextile&gt;complete -W '`rake—silent—tasks | cut -d ” ” -f 2`' -o default rake&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;However this didn&amp;#8217;t work for the namespaced tasks in a rails app like &lt;code&gt;rake test:units&lt;/code&gt;.  Searching a little further I found a reference to some code &lt;a href="http://i.conio.net/rake-completion.rb"&gt;Nicholas Seckar&lt;/a&gt; wrote on &lt;a href="http://project.ioni.st/post/212"&gt;project.ioni.st&lt;/a&gt;.  This used ruby to find the possible tasks for command completion.  This looked promising, but it still didn&amp;#8217;t work for namespaced tasks.  A little more googling led me to what looked like the perfect link: &lt;a href="http://www.webtypes.com/2006/03/31/rake-completion-script-that-handles-namespaces"&gt;Rake-completion script that handles namespaces&lt;/a&gt;.  Alas, it only handled one level of namespacing.  It worked nicely for &lt;code&gt;rake test:units&lt;/code&gt;, but &lt;code&gt;rake tmp:ses&amp;lt;TAB&amp;gt;&lt;/code&gt; would complete to &lt;code&gt;rake tmp:clear&lt;/code&gt; instead of &lt;code&gt;rake tmp:sessions:clear&lt;/code&gt;.  Also, &lt;code&gt;rake test:units &amp;lt;TAB&amp;gt;&lt;/code&gt; would complete to &lt;code&gt;rake test:units units&lt;/code&gt; instead of giving me all the tasks again, just in case you want to run multiple tasks form the command line.&lt;/p&gt;


	&lt;p&gt;So, now what?  Stand on the shoulders of others, naturally.  Here is what I&amp;#8217;m using now that handles multiple namespaces and multiple tasks per command line:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;notextile&gt;&lt;span class="comment"&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class="comment"&gt;# Complete rake tasks script for bash&lt;/span&gt;
&lt;span class="comment"&gt;# Save it somewhere and then add&lt;/span&gt;
&lt;span class="comment"&gt;# complete -C path/to/script -o default rake&lt;/span&gt;
&lt;span class="comment"&gt;# to your ~/.bashrc&lt;/span&gt;
&lt;span class="comment"&gt;# Nicholas Seckar &amp;lt;nseckar@gmail.com&amp;gt;&lt;/span&gt;
&lt;span class="comment"&gt;# Saimon Moore &amp;lt;saimon@webtypes.com&amp;gt;&lt;/span&gt;
&lt;span class="comment"&gt;# http://www.webtypes.com/2006/03/31/rake-completion-script-that-handles-namespaces&lt;/span&gt;

&lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;unless&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;file?&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="ident"&gt;join&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pwd&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Rakefile&lt;/span&gt;&lt;span class="punct"&gt;'))&lt;/span&gt;
&lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;^rake&lt;span class="escape"&gt;\b&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="constant"&gt;ENV&lt;/span&gt;&lt;span class="punct"&gt;[&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;COMP_LINE&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;]&lt;/span&gt;

&lt;span class="ident"&gt;after_match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$'&lt;/span&gt;
&lt;span class="ident"&gt;task_match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;after_match&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="ident"&gt;after_match&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 class="escape"&gt;\s&lt;/span&gt;$&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;after_match&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;last&lt;/span&gt;
&lt;span class="ident"&gt;tasks&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; `&lt;span class="ident"&gt;rake&lt;/span&gt; &lt;span class="punct"&gt;--&lt;/span&gt;&lt;span class="ident"&gt;silent&lt;/span&gt; &lt;span class="punct"&gt;--&lt;/span&gt;&lt;span class="ident"&gt;tasks`&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;&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="number"&gt;1&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;collect&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;line&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;line&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;split&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;tasks&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;select&lt;/span&gt; &lt;span class="punct"&gt;{|&lt;/span&gt;&lt;span class="ident"&gt;t&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 class="expr"&gt;#{Regexp.escape task_match}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="ident"&gt;t&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;task_match&lt;/span&gt;

&lt;span class="comment"&gt;# handle namespaces&lt;/span&gt;
&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;task_match&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 class="escape"&gt;\w&lt;/span&gt;:]+:)&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
  &lt;span class="ident"&gt;upto_last_colon&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$1&lt;/span&gt;
  &lt;span class="ident"&gt;after_match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="global"&gt;$'&lt;/span&gt;
  &lt;span class="ident"&gt;tasks&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;collect&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;t&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 class="expr"&gt;#{Regexp.escape upto_last_colon}&lt;/span&gt;([-&lt;span class="escape"&gt;\w&lt;/span&gt;:]+)$&lt;/span&gt;&lt;span class="punct"&gt;/)&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;&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="punct"&gt;:&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;tasks&lt;/span&gt;
&lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;/notextile&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;&lt;a href="http://onrails.org/files/rake-completion.rb"&gt;Enjoy&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Wed, 30 Aug 2006 21:34:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:4d2a935a-f730-406d-91af-b905e3b875cc</guid>
      <author>Lee Marlow</author>
      <link>http://onrails.org/articles/2006/08/30/namespaces-and-rake-command-completion</link>
      <category>Ruby</category>
      <category>Rails Tips</category>
      <category>Ruby On Rails</category>
    </item>
    <item>
      <title>Managing Rails Plugins dependencies</title>
      <description>&lt;p&gt;Rails has a nice plugin system allowing to add common code to a project. A plugin should really be independent from any other plugins. But we also use plugins to share code among different projects we are working on and our code depends on existing plugins.  The Rails development team want&amp;#8217;s to keep the plugin system simple and didn&amp;#8217;t provide an explicit way to handle these dependencies, which I believe is a good decision.  There is a solution. Simply name the plugins in order off the dependencies you have.  Let&amp;#8217;s assume you want to add &amp;#8220;my_very_own_plugin&amp;#8221; plugin that depends on the enumation_mixin,  then simply organize the /vendor/plugins folder as follows, et Voila!:.&lt;/p&gt;


&lt;pre&gt;
/myrailsapp
    /vendor
        /plugins
            /01_acts_as_taggable
            /01_enumations_mixin 
            /01_acts_as_versioned
            /02_my_very_own_plugin
&lt;/pre&gt;

	&lt;p&gt;If we look at the Rails::Initializer we can see why this works. Note, this is only an extract of the full class that Rails provides to bootstrap your rails applicaton. The sort on line 4 here after allows this trick.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;table class="typocode_linenumber"&gt;&lt;tr&gt;&lt;td class="lineno"&gt;
&lt;pre&gt;
1
2
3
4
5
6
7
8
&lt;/pre&gt;
&lt;/td&gt;&lt;td width="100%"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Rails&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Initializer&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;load_plugins&lt;/span&gt;
      &lt;span class="ident"&gt;find_plugins&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;configuration&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;plugin_paths&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;sort&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;path&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;load_plugin&lt;/span&gt; &lt;span class="ident"&gt;path&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
      &lt;span class="global"&gt;$LOAD_PATH&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;uniq!&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;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</description>
      <pubDate>Sat, 04 Mar 2006 09:26:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:321740a4-4c6c-41d7-b42a-c1c1e1164942</guid>
      <author>Daniel Wanja</author>
      <link>http://onrails.org/articles/2006/03/04/managing-rails-plugins-dependencies</link>
      <category>Rails Tips</category>
    </item>
  </channel>
</rss>
