<?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>The Dev Blog: Category Rails</title>
    <link>http://devblog.famundo.com/articles/category/rails</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Putting Family Management on Rails!</description>
    <item>
      <title>Improving Capistrano's put Command</title>
      <description>&lt;p&gt;&lt;a href="http://manuals.rubyonrails.com/read/book/17"&gt;Capistrano&lt;/a&gt; can be used for many tasks not directly related to deployment. One such task, which I use a lot, is getting content form staging servers, and putting it up on production servers. (See my post about &lt;a href="http://devblog.famundo.com/articles/2006/09/28/deploying-to-staging-and-production-with-capistrano"&gt;Deploying to Staging and Production with Capistrano&lt;/a&gt; for how to manage a staging/production split in capistrano.)&lt;/p&gt;

&lt;p&gt;Capistrano already has a put command for putting data on the server. The problem is that it expects a string, and so if uploading very large files (like a tar.gz of uploaded files, for example) this is VERY memory intensive. Imagine a 1GB file loaded into a string...&lt;/p&gt;

&lt;p&gt;As capistrano uses sftp if available, I wrote a small helper that does the same thing that get_file does in capistrano (this function is new in capistrano 1.4). Only it uploads files instead of downloading. There is one slight inconvenience with the code, in that it uploads the files one by one. Capistrano's put works in parallel, uploading to all servers concurrently. Usually this isn't a problem, as the upstrean bandwidth usually limit the speed of concurrent uploads anyway. A perfect solution will be to use a modified version of put, that reads the files a chunk at a time and uploads in chunks. For my need this is overkill.&lt;/p&gt;

&lt;p&gt;Open your deploy.rb recipies file, and add to it the following:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Capistrano::Actor&lt;/span&gt;

  &lt;span class="comment"&gt;# A saner put replacement that doesn't read the whole file into memory...&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;put_file&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;remote_path&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{})&lt;/span&gt;
    &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;put_file can only be used with SFTP&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;Capistrano&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;SFTP&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;fetch&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:sftp&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="ident"&gt;execute_on_servers&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;options&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;servers&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;servers&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;server&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;logger&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;info&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;uploading &lt;span class="expr"&gt;#{File.basename(path)}&lt;/span&gt; to &lt;span class="expr"&gt;#{server}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="ident"&gt;sftp&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;sessions&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;server&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;sftp&lt;/span&gt;
        &lt;span class="ident"&gt;sftp&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;connect&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;sftp&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;state&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="symbol"&gt;:open&lt;/span&gt;
        &lt;span class="ident"&gt;sftp&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;put_file&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;remote_path&lt;/span&gt;
        &lt;span class="ident"&gt;logger&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;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;done uploading &lt;span class="expr"&gt;#{File.basename(path)}&lt;/span&gt; to &lt;span class="expr"&gt;#{server}&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="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;To use it, call it just like get_file:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;desc&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Upload content to the remote server&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:upload_content&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:roles&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:app&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;put_file&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;content.tar.gz&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;content.tar.gz&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that it requires SFTP and will raise an error if not available. &lt;/p&gt;

&lt;p&gt;As of capistrano 1.4, you can now put shared capistrano code in /etc/capistrano.conf and it will loaded into every single project. You can put the above code in /etc/capistrano.conf and have it available everywhere.&lt;/p&gt;</description>
      <pubDate>Sat, 10 Mar 2007 05:00:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:56a272b9-b6ec-4b47-8cef-3b93a0846101</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/03/10/improving-capistranos-put-command</link>
      <category>Rails</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>How I learned to Love vim and Ditched Eclipse</title>
      <description>&lt;p&gt;For the longest time, since starting to work with Ruby and Rails, I used &lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt; with &lt;a href="http://www.radrails.org/"&gt;RadRails&lt;/a&gt; and &lt;a href="http://rubyeclipse.sourceforge.net/"&gt;RDT&lt;/a&gt; for coding. It's a pretty good IDE as far as IDEs go, and has nice Ruby/Rails tools to make development a lot easier. It also has a VERY nice subversion interface. One of the best I've seen as far as working the way I like to work.&lt;/p&gt;

&lt;p&gt;On the down-side, it's a performance hog and the editor itself is so-so in features. &lt;/p&gt;

&lt;p&gt;As I also do system admin chores on remote computers, I always use &lt;a href="http://www.vim.org"&gt;vi&lt;/a&gt; as well. And for many quick and dirty tasks, I use it even localy. Developing on a Linux machine, I always have some terminal windows open. Slowly I was pulled to do more things with vim, until I realized I prefer being in it than in Eclipse. Now I spend 90% of my time in vi, and most of my projects never even see Eclipse.&lt;/p&gt;

&lt;p&gt;Why did I switch? First and foremost vi as an editor is really good. And lets keep the vi/emacs wars for another time. I'm sure emacs is a really good editor as well. I just have to choose my tools. Can't use all of them. Second thing is speed. I NEVER have to wait for vi. It's always super fast and super responsive. &lt;/p&gt;

&lt;p&gt;But to make vi really useful with ruby and rails, some additions are needed. After all, I want synax highlighting, macros, easy way to open files, etc... So here is a list of vim scripts/plugins I use and recommed:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1567"&gt;rails.vim&lt;/a&gt;, &lt;a href="http://www.vim.org/scripts/script.php?script_id=403"&gt;eruby.vim&lt;/a&gt;, &lt;a href="http://www.vim.org/scripts/script.php?script_id=163"&gt;ruby-macros.vim&lt;/a&gt;, &lt;a href="http://www.vim.org/scripts/script.php?script_id=1662"&gt;rubycomplete.vim&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are many more additions and plugins on the &lt;a href="http://www.vim.org"&gt;vim site&lt;/a&gt;. Please note that some require the newer vim 7 to work.&lt;/p&gt;

&lt;p&gt;Another option is getting the rails-vim gem: sudo gem install vim-ruby. Please note you have to then run the vim-ruby-install.rb script to complete installation. &lt;/p&gt;

&lt;p&gt;So now that we have vim loaded with the additions, where do we start? Granted, the learning curve is steep! And don't get me started on using the esc key so much, that I keep doing it in Eclipse ;-). But as the editor is our main tool of work, learning it pays for itself later with productivity. vim has a lot of help written for it, and you can access it from within vim, but I prefer browsing it on the internet. So start with this &lt;a href="http://blog.interlinked.org/tutorials/vim_tutorial.html"&gt;short tutorial&lt;/a&gt;, and then bookmark the &lt;a href="http://vimdoc.sourceforge.net/htmldoc/help.html"&gt;main help file&lt;/a&gt;. And for the rails related parts, take a look &lt;a href="http://rails.vim.tpope.net/"&gt;here&lt;/a&gt;. Some of the things it does are just awesome. I open files faster with :Rfind than on a tree in the IDE browser! And check out the partial extraction. That one is unbelievable.&lt;/p&gt;

&lt;p&gt;vim also has the GUI version in gvim. And there you can open multiple tabs and have an IDE like file browser. Use that if you are more comfortable with the mouse and a real GUI. It's also easier to stasrt with as it has menus for the more common commands.&lt;/p&gt;

&lt;p&gt;One thing I did that really got me into more advanced editing, is deciding that whenever I want to do something, and I don't know how to best do it, I stop resorting to hacking a solution with the things I know, and look for the real solution. In a few short weeks I learned more than in the last 5 years...&lt;/p&gt;

&lt;p&gt;So give vi a try. Give yourself some learning time, and you'll never look back!&lt;/p&gt;

&lt;p&gt;BTW, I also use svn from the command line. I'ts so much faster than from Eclipse that I don't mind the few keystrokes it takes to get things done. It's so much faster!&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 08:03:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:61c9cd18-c0d1-4653-93ec-819cd7341004</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/03/08/how-i-learned-to-love-vim-and-ditched-eclipse</link>
      <category>Rails</category>
      <category>Ruby</category>
      <category>Programming</category>
      <category>Linux</category>
    </item>
    <item>
      <title>A New Face for Famundo</title>
      <description>&lt;p&gt;Finally after lots of work, the new &lt;a href="http://www.famundo.com"&gt;Famundo&lt;/a&gt; site is up an running. I believe it's VERY VERY nice. But go check it out! And do sign up for Famundo while you are there ;-).&lt;/p&gt;

&lt;p&gt;We also have the new mascots - same family as the one I have with the logo here on the blog. They are officially called the Famundudes (Famundad, Famunmom, Famunkids, and and the members of the community they interact with).&lt;/p&gt;

&lt;p&gt;There's an interesting story behind the site - the site is a fully page-cashed Rails application. And it has a small admin interface to manage all the changeable contents - like news and such. But the server I wanted to put it on started to miss-behave. So we generated all the needed pages by just accessing all the functionality, took the generated pages and uploaded it like a static content. Here's for you a nice little trick with rails for static sites.&lt;/p&gt;

&lt;p&gt;The site that will run live with rails is coming soon. And it has a few tricks up it's sleeve - like the ability to switch serving of assets from our servers or from S3, and the ability to cache the html directly into S3. I'll write more about it when it's up.&lt;/p&gt;

&lt;p&gt;Please go visit the &lt;a href="http://www.famundo.com"&gt;new site&lt;/a&gt; and sign up for Famundo.&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 00:46:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:f9a56c8a-fb0c-4d71-8394-3f03f7922ab3</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/03/08/a-new-face-for-famundo</link>
      <category>Rails</category>
      <category>S3</category>
    </item>
    <item>
      <title>libical + ruby + linux = nirvana</title>
      <description>&lt;p&gt;We have a need for strong ical support in &lt;a href="http://www.famundo.com"&gt;Famundo&lt;/a&gt;, and unfortunately all the ruby libraries for handling ical are nowhere near complete. A really great alternative is to use the well used and tested &lt;a href="http://www.aurore.net/projects/libical/"&gt;libical&lt;/a&gt;. This requires a wrapper that will let ruby use it, and the simplest way to do that, is to use &lt;a href="http://www.swig.org/"&gt;SWIG&lt;/a&gt; to generate the wrapper, then compile it into an extension ruby can use.&lt;/p&gt;

&lt;p&gt;Lucky for me, Rob Kaufman in &lt;a href="http://rubyforge.org/pipermail/rcalendar-devs/2006-October/000050.html"&gt;this post&lt;/a&gt; contributed most of what's needed to get it going with SWIG. But it won't work on Linux. It seems it was compiled on a Mac, and trying to compile it on Linux just won't work. &lt;/p&gt;

&lt;p&gt;Lucky for you, I have here the steps to make it compile on Linux (and probably any other system that supports the standard make tools). So lets jump right into the instructions.&lt;/p&gt;

&lt;p&gt;1. Get the &lt;a href="http://www.aurore.net/projects/libical/"&gt;latest libical files&lt;/a&gt; and the zip file attached to &lt;a href="http://rubyforge.org/pipermail/rcalendar-devs/2006-October/000050.html"&gt;Rob's post&lt;/a&gt;. You will also need to get the latest &lt;a href="http://www.swig.org/survey.html"&gt;SWIG&lt;/a&gt; if it's not yet installed on your system. &lt;/p&gt;

&lt;p&gt;2. Compile and install libical: &lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;    tar tar xif libical-0.26-6.aurore.tar.bz2
    cd libical-0.26
    ./configure # you can run ./configure --help to see more options  
    make
    sudo make install &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;3. Extract the files of the ruby SWIG based wrapper:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;    unzip libical-ruby.zip
    cd libical-ruby/swig/
    make clean&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;4. Create an &lt;strong&gt;extconf.rb&lt;/strong&gt; file to generate the makefile. The file should have the following in it:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;    &lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;mkmf&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

    &lt;span class="ident"&gt;have_library&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;pthread&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;have_library&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;ical&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;icaltime_null_time&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;have_library&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;icalss&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;icalset_new_dir&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;

    &lt;span class="ident"&gt;create_makefile&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;LibicalWrap&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;5. Generate the needed wrapper file using swig:
    swig -ruby -o LibicalWrap.c ical.i&lt;/p&gt;

&lt;p&gt;6. Run: &lt;strong&gt;ruby extconf.rb&lt;/strong&gt; This should generate the needed Makefile. If it fails, you might not have the libraries from libical installed correctly. If that is the case, redo step &lt;strong&gt;2&lt;/strong&gt; above, but use the ./configure command with different parameters, to point it to the correct lib directory. For example, on 64bit FC4, you might want to try with this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./configure --libdir=/usr/lib64 --includedir=/usr/include
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;7. Compile and install the wrapper:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;    make clean
    make 
    sudo make install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;8. We're done. You can now launch irb and type &lt;strong&gt;include 'LibicalWrap'&lt;/strong&gt;, and you are ready to go. With the libical-ruby.zip file, there are also some tests you can run on it to see if it works. And some ruby helpers to facilitate working with the library, as the library is C based, and so doesn't have nice class representations for ical objects.&lt;/p&gt;

&lt;p&gt;Now that we have a good ical library, time to start using it. But that's outside the scope of this post.&lt;/p&gt;

&lt;p&gt;Let me know if you have problems getting it to work. I'll be glad to help.&lt;/p&gt;</description>
      <pubDate>Thu, 22 Feb 2007 08:25:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:410446b7-7089-4166-900f-2f53d12fc83c</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/22/libical-ruby-linux-nirvana</link>
      <category>Rails</category>
      <category>Ruby</category>
      <category>Linux</category>
    </item>
    <item>
      <title>A New Version of acts_as_rated plugin</title>
      <description>&lt;p&gt;I just uploaded to &lt;a href="http://acts-as-rated.rubyforge.org/"&gt;rubyforge&lt;/a&gt; a new version of the acts_as_rated plugin.&lt;/p&gt;

&lt;p&gt;This version is thanks to Tiago Serafim who tested it and proposed the needed changes needed to make it work with MySQL.&lt;/p&gt;

&lt;p&gt;Tiago also proposed a new method: &lt;strong&gt;rated_by?(rater)&lt;/strong&gt; that returns true if the object is rated by the passed rater.&lt;/p&gt;

&lt;p&gt;Thanks Tiago!&lt;/p&gt;

&lt;p&gt;As usual full testing is provided. There is one failure in MySQL testing due to strangeness in MySQL average handling. They do fully pass with Postgres.&lt;/p&gt;</description>
      <pubDate>Sun, 18 Feb 2007 08:09:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:3432bf97-11e8-49b7-acc8-3e27d4e14c9f</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/18/a-new-version-of-acts_as_rated-plugin</link>
      <category>acts_as_rated</category>
      <category>Rails</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>I Love Ruby on Rails But...</title>
      <description>&lt;p&gt;...The last thing I want is start a language/framework war.&lt;/p&gt;

&lt;p&gt;A funny little post I did about a &lt;a href="http://devblog.famundo.com/articles/2006/11/09/a-world-time-server-in-one-line-of-rails"&gt;world time server in a single line of rails code&lt;/a&gt; was posted on &lt;a href="http://www.dzone.com"&gt;dzone&lt;/a&gt; with some comment about ruby, php and java, and caused a lot of heated comments.&lt;/p&gt;

&lt;p&gt;So hereby I declare: &lt;strong&gt;I Love Rails&lt;/strong&gt;, but I'm the last one to think that rails is the Holly Grail of languages and framework. It's really well written, it's a joy to write web apps in, but it's not the only game in town.&lt;/p&gt;

&lt;p&gt;You like php? Love Java? Think C# is the best thing since sliced bread? All the power to you! Use them and enjoy them. And I'm sure there are enough projects where it makes more sense to to use those languages/framework.&lt;/p&gt;

&lt;p&gt;I'm a true believer in one thing: know has many tools as you can comfortably manage, and use the one best suited for the task. For years I programmed in in C/C++ on Win32. I know a very large number of languages, and worked professionally with C/C++, Pascal, Perl, PHP, Ruby, dBaseII - dBaseIV (I think I'm giving away my ancientness here...) and a lot of other languages. Really, even assembler. Heck, I'm teaching my 9 year old daughter to program in Logo.&lt;/p&gt;

&lt;p&gt;So please, don't use my posts for language wars, we have enough of those already.&lt;/p&gt;</description>
      <pubDate>Sat, 17 Feb 2007 07:00:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:73fb969f-3bb5-434a-91bf-448635a4a685</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/17/i-love-ruby-on-rails-but</link>
      <category>Rails</category>
      <category>Ruby</category>
      <category>Programming</category>
    </item>
    <item>
      <title>Adding More Control to the Displayed Data in AjaxScaffold</title>
      <description>&lt;p&gt;Wow, my 4th post of &lt;a href="http://www.ajaxscaffold.com"&gt;AjaxScaffold&lt;/a&gt;! This time a small change in it to let you better control the query used to retireve the data into the grid.&lt;/p&gt;

&lt;p&gt;The problem I'm trying to solve, is including a join to the query used when laoding the grid. It's especially useful when doing filtering and searching. My example is the User model that has also a UserSetting association. I want the query to include a join to the user_settings table, as I want to be able to search on the fields from the joined table.&lt;/p&gt;

&lt;p&gt;The easiest way to search/filter on AjaxScaffold is to define in your controller the method:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;conditions_for_#{plural_name}_collection&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 value returned from this method is assigned internally by AjaxScaffold to the &lt;strong&gt;:conditions&lt;/strong&gt; option of find. But if you now try to write a condition like:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;lower(name) LIKE ? OR lower(city) LIKE ?&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;city&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; 

&lt;p&gt;With city coming from the user_settings table, it will fail, as this field isn't part of the query. So we need to also change the query to include additional query parameters. All we need to do, is add another callback like the one above, that will let us adjust the options used for the find:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;adjust_options&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt; &lt;span class="ident"&gt;options&lt;/span&gt; &lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;options&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;merge!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:joins&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;inner join user_settings on user_settings.user_id = users.id&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt; &lt;span class="punct"&gt;)&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To implement the change, we need to add a call to &lt;strong&gt;adjust&lt;em&gt;options&lt;/strong&gt; before using the options in the code. Look in &lt;strong&gt;vendor/plugins/ajaxscaffoldp/lib/ajax&lt;/em&gt;scaffold&lt;em&gt;plugin.rb&lt;/strong&gt; for the function &lt;strong&gt;def #{prefix}table&lt;/em&gt;setup&lt;/strong&gt;. Around line 200, after the code:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&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;:order&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;order&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
                &lt;span class="symbol"&gt;:conditions&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;conditions_for_&lt;/span&gt;&lt;span class="comment"&gt;#{plural_name}_collection,&lt;/span&gt;
                &lt;span class="symbol"&gt;:direction&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;current_sort_direction&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;),&lt;/span&gt;
                &lt;span class="symbol"&gt;:per_page&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="comment"&gt;#{rows_per_page} }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add the following:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;adjust_options&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;options&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;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;respond_to?&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;adjust_options&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a hack, as I didn't want to do a big patch when a new and highly modified version AjaxScaffold is on it's way. Should be fine as an interim solution.&lt;/p&gt;</description>
      <pubDate>Thu, 15 Feb 2007 05:00:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:be00d84e-da9d-467f-a08c-a0209b2de5c0</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/15/adding-more-control-to-the-displayed-data-in-ajaxscaffold</link>
      <category>Rails</category>
    </item>
    <item>
      <title>Testing: DRYing the Asserting of Array Similarity</title>
      <description>&lt;p&gt;After one too many instances of:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;ph&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;contacts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:john&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;phones&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;p&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;number&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;assert_equal&lt;/span&gt; &lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ph&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;
&lt;span class="ident"&gt;assert_equal&lt;/span&gt; &lt;span class="punct"&gt;[].&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;12345&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;56789&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;67890&lt;/span&gt;&lt;span class="punct"&gt;']&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="ident"&gt;ph&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I decided it's time to DRY up array similarity checking. Using &lt;strong&gt;assert_equal&lt;/strong&gt; on arrays is a so-so solution, given that you don't always know if the order in the array is the same. Returning  an array from a &lt;strong&gt;has_many&lt;/strong&gt; collection has a non-deterministic order by default. So asserting that what we get from a collection is a bit painfull, and require the code above to make absolutely sure we got what we wanted.&lt;/p&gt;

&lt;p&gt;To make my life easier and DRYer, I added the following function to the &lt;strong&gt;test/test_helper.rb&lt;/strong&gt; file:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;assert_array_similarity&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;expected&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;actual&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;message&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;full_message&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;build_message&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;message&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;&amp;lt;?&amp;gt; expected but was&lt;span class="escape"&gt;\n&lt;/span&gt;&amp;lt;?&amp;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;expected&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;actual&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="ident"&gt;assert_block&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;full_message&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;expected&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt;  &lt;span class="ident"&gt;actual&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;expected&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="ident"&gt;actual&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="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now testing for array similarity is simply:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;assert_array_similarity&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;12345&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;56789&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;67890&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="ident"&gt;contacts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:john&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;phones&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;p&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;number&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not bad, 1 line instead of 3 and better error reporting to boot.&lt;/p&gt;</description>
      <pubDate>Tue, 13 Feb 2007 06:21:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:1aad69f7-4179-47c2-9e59-dfb613871241</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/13/testing-drying-the-asserting-of-array-similarity</link>
      <category>Rails</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>Solving Tough Deploy Problems with FastCGI and Rails</title>
      <description>&lt;p&gt;Sometimes you deploy a new rails app with fastcgi and it just doesn't work. No errors, no logs, nothing! Usually it's some permission problems, or a missing file. But it also might be some odd problems like a wrong shebang line somewhere.&lt;/p&gt;

&lt;p&gt;The problem with those is that you get no error and no indication of what went wrong. When using the spawner script you might see the actual appication being launched again and again by the spawner, only to die immediately.&lt;/p&gt;

&lt;p&gt;I found a neat trick to see what's really going on when this happens. On the deployment directory (on the server you deploy to) edit the file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;vendor/rails/railties/lib/commands/process/spawner.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And look for the lines that redirect STDIN, STDOUT and STDERR to /dev/null. Comment those lines, and launch the spawner from the command line. The errors will start flowing into the console. Just look at them and you should be on your way to solving the problem.&lt;/p&gt;

&lt;p&gt;Here is some code from the spawner file with the lines commented out:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;daemonize&lt;/span&gt; &lt;span class="comment"&gt;#:nodoc:&lt;/span&gt;
  &lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;fork&lt;/span&gt;                   &lt;span class="comment"&gt;# Parent exits, child continues.&lt;/span&gt;
  &lt;span class="constant"&gt;Process&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;setsid&lt;/span&gt;                 &lt;span class="comment"&gt;# Become session leader.&lt;/span&gt;
  &lt;span class="ident"&gt;exit&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;fork&lt;/span&gt;                   &lt;span class="comment"&gt;# Zap session leader. See [1].&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;chdir&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;                  &lt;span class="comment"&gt;# Release old working directory.&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;umask&lt;/span&gt; &lt;span class="number"&gt;0000&lt;/span&gt;                &lt;span class="comment"&gt;# Ensure sensible umask. Adjust as needed.&lt;/span&gt;
&lt;span class="comment"&gt;#  STDIN.reopen &amp;quot;/dev/null&amp;quot;       # Free file descriptors and&lt;/span&gt;
&lt;span class="comment"&gt;#  STDOUT.reopen &amp;quot;/dev/null&amp;quot;, &amp;quot;a&amp;quot; # point them somewhere sensible.&lt;/span&gt;
&lt;span class="comment"&gt;#  STDERR.reopen STDOUT           # STDOUT/ERR should better go to a logfile.&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <pubDate>Sun, 11 Feb 2007 22:13:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:cef13b21-7a02-4a99-ab9a-2d7f143c2098</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/11/solving-tough-deploy-problems-with-fastcgi-and-rails</link>
      <category>Rails</category>
      <category>Ruby</category>
    </item>
    <item>
      <title>Hooked on Testing</title>
      <description>&lt;p&gt;I am now officially completely hooked on testing! The last few weeks proved to me how empowering tests can be. And not just for sleeping good at night, it has some other advantages that make every minute spent on testing worth the time.&lt;/p&gt;

&lt;p&gt;When Rails 1.2 was &lt;a href="http://feeds.feedburner.com/~r/RidingRails/~3/77607677/rails-1-2-rest-admiration-http-lovefest-and-utf-8-celebrations"&gt;released&lt;/a&gt;, I wanted to switch over a few of my applications. The ones that have a full test suite where a no brainer. Switch rails version (I keep it on &lt;a href="http://devblog.famundo.com/articles/2007/01/24/managing-deployments-of-plugins"&gt;my own repository&lt;/a&gt;), run tests, fix what's broken, and off we go! Even the small apps had some issues with the migration - some small changes that needed to be done. With the tests it's easy. But some other old apps that didn't have full testing - I just decided to wait a bit. It will require too much manual labor to make sure everything is in place. I had a similar experience with switching plugin versions. &lt;/p&gt;

&lt;p&gt;Now every change I need to do, I can easily verify that it will work. No need for guessing or manual testing (which isn't reliable enough). This is a huge improvement from what I was used to in past, especially in my C++ development days, where testing was always external to the application, and it's so much better!&lt;/p&gt;

&lt;p&gt;Regarding TDD (Test Driven Development) - I think it's amazingly well suited for functional testing, and even more so for integration testing. For models I still prefer to model the structure first and then test, especially for complex data models. It is still more natural for me to think of the structure. Maybe it's something I will change with time as well.&lt;/p&gt;

&lt;p&gt;My next goal in testing is getting &lt;a href="http://www.openqa.org/selenium/"&gt;Selenium&lt;/a&gt; going and doing with it acceptance and integration testing.&lt;/p&gt;

&lt;p&gt;A really good book all around, but on tests it really shines, is &lt;a href="http://www.apress.com/book/bookDisplay.html?bID=10178"&gt;Beginning Ruby on Rails E-Commerce&lt;/a&gt;. They practice TDD from the first step of development, and really make the process clear. I highly recommend it even if you don't need an introduction to Rails or learn e-commerce. It's worth the money just to learn testing and TDD. They even have a chapter devoted to Selenium. It's now the third book I recommend for Rails developers: &lt;a href="http://www.pragmaticprogrammer.com/titles/ruby/index.html"&gt;Programming Ruby&lt;/a&gt;, &lt;a href="http://www.pragmaticprogrammer.com/titles/rails2/index.html"&gt;AWDR&lt;/a&gt; and &lt;a href="http://www.apress.com/book/bookDisplay.html?bID=10178"&gt;Beginning Ruby on Rails E-Commerce&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Wed, 07 Feb 2007 05:50:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:65203c34-18ea-46ff-82df-64116d11ae6f</guid>
      <author>guy.naor@famundo.com (Guy Naor)</author>
      <link>http://devblog.famundo.com/articles/2007/02/07/hooked-on-testing</link>
      <category>Rails</category>
    </item>
  </channel>
</rss>
