The semi-colon separator in resource URIs: JNWI

Thursday, March 29

I’m quite thrilled by a recent commit to Rails: the dropping of the semi-colon separator in resource routes. Before now, the semi-colon was being used to distinguish non-CRUD actions on resources. From DHH’s commit message on the subject:

It (the semi-colon) was a neat idea, but lots of the non-crud actions turned out not to be RPC (as the ; was primarily intended to discourage), but legitimate sub-resources, like /parties/recent, which didn’t deserve the uglification of /parties;recent. Further more, the semicolon caused issues with caching and HTTP authentication in Safari. Just Not Worth It [DHH]

The ‘edit’ action is a good example. Edit isn’t really a CRUD action. The CRUD action that ultimately works on edit is update, however, we need to edit to display the html forms we need to modify the resource in our browsers.

I’ll confess to never having liked the semi-colon. It was just too ugly. I understand that the ugliness was, of course, intentional, but it still made me throw up in my mouth a little.

I also enjoyed DHH’s coining of the acronym: JNWI (Just Not Worth It).

Generating a SPECDOC from tests in Rails

Thursday, March 29

So I’ve been using RSpec for a while, but sometimes its integration with the Rails framework frustrates me. On the last project I started, I decided not to make waves and just use Test::Unit, which is the Rails default. It’s just easier that way.

Still, there are some things I like about RSpec. One of these things is the ‘this should do that’ syntax, which feels more natural to me. So, ever since I’ve been back on the Test::Unit bandwagon, I’ve been writing by tests in a test_should_do_something_amazing style. (I’ve noticed that a lot of Rails developers are using this style as of late, and even the test stubs that the Rails generators spit out are using this style as well).

Another thing I like about RSpec is its ability to produce a specification document based on your tests, which it calls a SPECDOC. Inspired by the recently added rake task notes, (which enumerates all the commented fixmes, todos, etc. in your app), I figured a SPECDOC enumerator wouldn’t be too hard to add. Here’s a stab at a rake task that does the job. This will rip through your tests, look for specification-style tests, and generate a SPECDOC in the docs directory.

desc "Generate specdoc-style documentation from tests" 
namespace :doc do
  task :specs do
    puts 'Started'

    timer, count = Time.now, 0

    File.open(RAILS_ROOT + '/doc/SPECDOC', 'w') do |file|
      Dir.glob('test/**/*_test.rb').each do |test|
        test =~ /.*\/([^\/].*)_test.rb$/
        file.puts "#{$1.gsub('_', ' ').capitalize} should:" if $1
        File.read(test).map {|line| /test_should_(.*)$/.match line }.compact.each do |spec|
          file.puts " * #{spec[1].gsub('_', ' ')}" 
          sleep 0.001; print '.'; $stdout.flush; count += 1
        end
        file.puts
      end
    end

    puts "\nFinished in #{Time.now - timer} seconds.\n" 
    puts "#{count} specifications documented" 
  end
end

Place this snippet in a file called specdoc.rake and throw it in lib/tasks and you’ll be good to go. Run it using rake doc:specs as follows:

$ rake doc:specs
Started
..............................................................................
..............................................................................
......................................
Finished in 2.308674 seconds.

194 specifications documented

The dots and chatter are all just eye-candy, but I thought I’d add them in for good measure. I’m thinking of removing it, though, just to cut down on the code. The SPECDOC will be output to the doc directory.

$ cat doc/SPECDOC 
Memberships controller should:
 * require login for protected actions
 * not require login for public actions
 * create and send invitations

Users controller should:
 * create user
 * require unique login on create
 * require password on create
 * require password confirmation on create
 * require email on create
 * require valid email on create
 * require authentication
 * accept authentication
 * get new user
 * show user
 * update user
...

Note that this will only document tests that begin with the aforementioned test_should_ prefix. Test cases that don’t use this convention are ignored, though you could tweak the regex to include them.

My regex-fu may be lacking, so if anyone has any suggestions for how I could make this better, don’t hesitate to pipe up.

MacPorts and Google's SOC

Monday, March 26

I just read that MacPorts has been selected by Google as a member project of the Summer of Code for 2007.

http://trac.macosforge.org/projects/macports/wiki/SummerOfCode

There are a bunch of tasks on the list, including a re-write of the dependencies system, as well as a GUI, but of particular interest to me is Task #4: Binaries.

MacPorts project does not provide binaries yet (installation of software without compiling them). This project consists in working in concert (or cooperatively) with whomever does (virtual chroot) to setup a mechanism to automatically build packages, send reports on failures and implement a distribution mechanisms to allow users to fetch binary packages.

Before switching to OS X, I was a Linux user. Specifically, I used Ubuntu/Debian, whose apt (Advanced Packaging Tool) package manager is simply wonderful. I realize now how spoiled I was. If OS X lacks anything, it’s a decent package manager. Of course, there’s Fink (which is like apt), but for various reasons I’ve had better luck with (Mac|Darwin)Ports and have developed a preference for it. Now that Apple is (somewhat) behind the MacPorts project, I’m hoping that an ‘official’ package manager will emerge. MacPorts with support for binary ports would rock.

Installing gems with capistrano

Friday, March 16

Ever go to deploy a copy of your application and realize that you don’t have all the required gems installed? This is what I do to handle such a precarious situation via capistrano.


desc 'Installs required gems for this application'
task :install_gems, :roles => :app do
  # Add all gems required by your application here
  gems = %w(
    aws-s3
    libxml-ruby
    redcloth
    tzinfo
  )

  sudo "gem install -y --no-rdoc --no-ri #{gems.join(' ')}" do |channel, stream, data|
    print data if stream == :out
    channel.send_data($stdin.gets) if data =~ /^>\s/
  end
end

The channel.send bit is so you can respond to gems that have multiple versions available (i.e., mongrel). I’m also skipping rdoc and ri for this since I generally don’t care about having the documentation on the production server and it’s a bit faster when you skip them.

Oh, and if you wanted this to happen automatically after your cap setup, you could invoke it using (wait for it) the after_setup callback.

I’m sure there are improvements that could be made to this code, so suggestions are welcome.