I haven’t been blogging much lately. I’ve been too busy with some vacation time and, of course, work.

This is going to change, but it’s not all going to happen on Programblings.

It’s been a long time coming, but giraffesoft finally has a blog. We’re going to kickstart our blog with a week of open source releases.

At giraffesoft we like DRY code. We all know that creating Rails plugins is barely more work than actually implementing the functionality inside of a specific application. For that reason, we create plugins all the time when working on projects.

So this week, we’re going to polish up a few of them – big and small – and officially introduce them to the world.

For now, please let me direct you to the brand-spanking new giraffesoft blog.

If you’re too lazy to read the introductory post, here’s the punchlines:

Tonight I’m trying conciseness.

Editor’s note: I failed.

I recently decided to test my git_remote_branch gem with Ruby 1.9, for the heck of it. Well, I was making sure it ran on a bunch of platforms: Windows, Ruby 1.8.7 and with the most recent Git version (1.6.0.2, get it). So it seemed fitting to check it out under Ruby 1.9.

On Leopard, the only missing dependency to Ruby 1.9 is readline 5.2. This article will present the installation of both. And help heat up your apartment.

Linux too

These instructions will mostly work on Linux as well (tried on Ubuntu). There will be a few minor differences though.

  • Make sure you download the original readline from the gnu site and patch it yourself;
  • Uninstall older versions of Ruby1.9;
  • Make sure you have the basic dev tools installed, like gcc and make;
  • Skip the part about installing Xcode :-)

Prerequisites

You first need to have the Apple developer tools installed on your mac. They’re available on your installation CD. Put it in, run the installer. It’s pretty straightforward.

# in your terminal
open /Volumes/Mac\ OS\ X\ Upgrade\ DVD/Optional\ Installs/Xcode\ Tools/XcodeTools.mpkg 

If you don’t compile your own stuff often, you may have to set up your PATH variable in your ~/.bash_profile.

# file  ~/.bash_profile
export PATH="/usr/local/bin:$PATH"

Now, prepare a working directory to keep the source close to the corresponding executables.

# in your terminal
sudo mkdir -p /usr/local/src
sudo chgrp admin /usr/local/src
sudo chmod -R 775 /usr/local/src
cd /usr/local/src

Once you’ve set yourself up, if you don’t care about the details, you can skip to the end for the Cliff’s notes.

Installing readline

This one’s not as straightforward as it could have been. The gzipped readline library available on the gnu site is 12 patches behind. It so happens that the 12th patch fixes a problem with compilation under OS X. So I applied all 12 to the code and repackaged it. The example uses that file, compiled by me.

You can also do do the patching by yourself if you so wish. Here’s where you can download the readline code:

So let’s get on with the instructions to install readline from my patched package:

# in your terminal
curl -O http://s3.amazonaws.com/webmat-public/readline-5.2-patch012.tar.gz
md5 readline-5.2-patch012.tar.gz
# should be a9f37d2a22d181f8c23c6a320907917d
tar xzf readline-5.2-patch012.tar.gz
cd readline-5.2-patch012

./configure --prefix=/usr/local
make
sudo make install
cd ..

Build Ruby 1.9

Build options

Note that here you have a few options as to how you want to distinguish your 1.9 stack from your main 1.8 one.

In the following example, I build Ruby with the ‘1.9’ suffix, which means all executables will be suffixed with 1.9: ruby1.9, gem1.9, irb1.9, rake1.9 and so on. This approach is ideal for casual use of two versions side by side. If you don’t care about the details, skip right over the next paragraph.

The industrial approach would be to put ruby in a non standard directory and only add it to your path when you want to use that version (or use the explicit path to invoke executables). To go industrial, you can simply use the --prefix=/usr/local/ruby1.9 option and then drop the --program-suffix argument when you run configure for Ruby. This setup is ideal if you really want to have a bunch of versions living side by side (e.g. all 1.9 versions as well as 1.8.7 in addition to the current 1.8.6).

Actual installation of Ruby 1.9

Pick the most recent version or Ruby 1.9 on the ftp server. At the time of writing, 1.9.1-preview1 is the most recent.

So, still from /usr/local/src:

# in your terminal
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.gz
tar xzvf ruby-1.9.1-preview1.tar.gz
cd ruby-1.9.1-preview1
./configure --prefix=/usr/local --program-suffix=1.9 --enable-pthread --with-readline-dir=/usr/local --enable-shared
make
sudo make install

Now you’re about to see some of the funniest looking progress indicators around. Ruby’s about making the programmer happy, and it delivers even in the details!

Note: the recent source packages of Ruby1.9 automatically include the documentation, as the end of the make install attests.

Try Ruby 1.9

# in your terminal
ruby1.9 --version
gem1.9 --version
irb1.9

Once inside the Ruby interactive shell,

# in irb1.9
RUBY_VERSION
#=> "1.9.1"
stabby = ->(msg='inside the stabby lambda') { puts msg }
stabby.call
# => "inside the stabby lambda"
stabby.call 'hello world'
# => "hello world"

Yep, Ruby 1.9 introduces the very cool stabby lambda syntax. Ruby 1.8’s lambdas couldn’t have optional parameters (unless you fiddled with *args). 1.9’s stabby lambdas can, with a syntax as clean as a simple method definition, as you just experimented.

Now install the gems you use everyday (or kindly make available to your peers) and help make them 1.9 compatible.

For the sake of the stabby lambda!

That’s it! (Except for those who skipped to the Cliff’s Notes).

Cliff’s Notes

# Install patched readline
cd /usr/local/src
curl -O http://s3.amazonaws.com/webmat-public/readline-5.2-patch012.tar.gz
md5 readline-5.2-patch012.tar.gz
# should be a9f37d2a22d181f8c23c6a320907917d
tar xzf readline-5.2-patch012.tar.gz
cd readline-5.2-patch012
./configure --prefix=/usr/local
make
sudo make install
cd ..

# Install Ruby1.9
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.gz
tar xzvf ruby-1.9.1-preview1.tar.gz
cd ruby-1.9.1-preview1
./configure --prefix=/usr/local --program-suffix=1.9 --enable-pthread --with-readline-dir=/usr/local --enable-shared
make
sudo make install

# Try Ruby 1.9
ruby1.9 --version
gem1.9 --version
irb1.9

# in irb1.9
RUBY_VERSION
stabby = ->(msg='inside the stabby lambda') { puts msg }
stabby.call
stabby.call 'hello world'

To install any ruby gem which has a command-line interface on Ubuntu 8.10, you have to add a path to your PATH environment variable. In your .bashrc file, add the following line:

export PATH=$PATH:/var/lib/gems/1.8/bin

Also worth noting is the fact that the default ruby interpreter on 8.10 is back to the 1.8 branch: it’s 1.8.7 (1.9 was the default on 8.04 iirc). 1.9  also be installed right besides 1.8.

$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]
$ ruby1.9 --version
ruby 1.9.0 (2008-06-20 revision 17482) [i486-linux]

Neither comes installed by default, however. You must install them explicitly.

sudo aptitude install ruby irb rubygems

While I’m at it, why not mention that rubygems 1.2.0 is installed by default. It doesn’t want to update to 1.3.0 with the usual “gem update –system” command. Since it’s not my main machine I didn’t investigate further, but the suggestion is to use apt-get or aptitude. The repos don’t seem to be up to date with 1.3.0, but rather with a version named something like 1.3.0really1.2.0.

In praise of Shoulda macros

Shoulda contexts let you to share setup code between different tests. This is for me one of Shoulda’s most attractive features.

When you combine this with the technique of defining your own macros to encapsulate assertions or setups that come up often, you end up with seriously DRY and readable tests.

I see a few different kinds of Shoulda macros:

Assertion macros

Assertions macros often begin with should_. They encapsulate one or a few assertions.

For ActiveRecord models:

should_require_attributes :name, :phone_number

They may even accept a block and do assertions on its execution, like should_raise:

should_raise(LoadError, :message => /vespene/) do
  require "more vespene gas"
end

To learn more about assertion macros, you can take a look at Shoulda macros allows you to embrace your inner slacker by Josh Nichols. Inner slacker? I’m right there!

Setup macros

This kind of macro encapsulates a setup that comes up often in your test suite. One inspired by Restful Authentication’s login_as helper method could be used like this:

logged_in_as :mat do
  # Shoulda tests
end

These kinds of macros accept a block that defines more Shoulda tests, rather than a block of code testing your app per se.

Turnkey macros

Turnkey macros are beefed up assertion macros. The main difference is their extent. They contain many contexts and a lot of should blocks. They usually accept substantial options hashes or are configured with a setup block. Like should_be_restful in the following example, inspired by the Shoulda documentation:

logged_in_as :stranger do
  should_be_restful do |resource|
    resource.create.params   = { :subject => "test", :body => "message" }
    resource.denied.actions  = [:edit, :update, :destroy]
    resource.denied.redirect = "login_url"
    resource.denied.flash    = /only the owner can/i
  end
end

Two Shoulda best practices around setup macros

This article is specifically about setup macros.

Here’s the implementation of a pretty generic Shoulda macro I could define in my test_helper*. This is an implementation of the macro I mentioned at the beginning:

# Sets the current person in the session from the person fixtures.
def self.logged_in_as(person, &block)
  context "logged in as #{person}" do
    setup do
      @request.session[:person] = people(person).id
    end

    yield
  end
end

Which can then be used like this in any controller test:

logged_in_as :mat do
  # tests for users
end

logged_in_as :admin do
  # tests for admin
end

Setup macros have a very subtle catch, however. Here’s a modified version of the first example above:

logged_in_as :mat do
  setup do
    @request.session[:last_login] = Time.now
  end
  # Some tests
end

The setup block you see here is never going to be executed. Why?

If we were to replace the logged_in_as macro by the actual code it contains, here’s what it would look like:

context "logged in as #{person}" do
  setup do
    @request.session[:person] = people(person).id
  end
  setup do
    @request.session[:last_login] = Time.now
  end
  # Some tests
end

Does that make sense? Not so sure.

Shoulda doesn’t like to have multiple setup blocks for a given context. That part does make sense.

Best practice #1: Always describe the situation with a context.

You should always describe the situation in which your test takes place (what your setup is doing) with a context.

logged_in_as :mat do
  context "with last login set to now" do
    setup do
      @request.session[:last_login] = Time.now
    end
    # Some tests
  end
end

Fair enough. We blame it on the user of the macro :-)

Since we’re using Ruby, most of us are probably in agreement with Matz’ “Make the programmer happy” motto.

So can we also solve the problem from the other end? Create a setup macro that supports a direct inner setup block? Of course we can, this is Ruby, not VB.

Best practice #2: Create setup macros that support a second setup block

A setup is grafted to a context that describes it. As the creator of the macro, I don’t know what crazy setup blocks programmers will put inside their macro. So I simply create a mute context:

# Sets the current person in the session from the person fixtures.
def self.logged_in_as(person, &block)
  context "logged in as #{person}" do
    setup do
      @request.session[:person] = people(person).id
    end

    context '' do
      yield
    end
  end
end

Now my macro supports the following test without a hitch:

logged_in_as :mat do
  setup do
    @request.session[:last_login] = Time.now
  end
  # Some tests
end

And of course, programmers who stick to best practice #1 can still write a cleaner test without a problem. The awesomeness of contexts lies in the fact that they can be nested:

logged_in_as :mat do
  context "with last login set to now" do
    setup do
      @request.session[:last_login] = Time.now
    end
    # Some tests
  end
end

Conclusion

Best practice #1 is simple. A setup block should be described by its encompassing context. It’s a question of readability. Nesting a setup block immediately inside a Shoulda macro is a dubious practice.

Best practice #2 is a more pragmatic solution to the problem. Ok, nesting a block right inside a macro isn’t always the best idea.

But when you don’t have the macro right under your nose, it may take you a while before you think about looking at said macro. I don’t know about you, but I have a tendency to have a great deal of confidence in macros that work well across my test suite.

So after you’ve spent an hour questioning Shoulda (or your sanity, or whether you should have become a gardener instead of a software developer) because your setup block isn’t executing, best practice #2 starts to make sense.

It may or may not be necessary in all your setup macros. I find it’s especially useful for macros that are generic enough to be used across your test suite. Or most of all, in setup macros you will share with the world.

Best practice #2 makes setup macros bulletproof to the problem of multiple setups.

Now go refactor your setup macros!

To learn more about Shoulda, check out Thoughtbot’s comprehensive documentation.

* A note on where to define Shoulda macros. Shoulda 2 can now auto load macros that are in the right location. This will help you keep your test_helper cleaner. Read more about it succinctly in Shoulda can automatically load custom macros by Josh Nichols or in the Shoulda 2.0 release post.

If you use Shoulda and, like me, you hate Test::Unit’s assert_raise(), I may have something of interest for you.

Why the hate?

Well, assert_raise accepts an *args list of exception types.

If you don’t pass any, you get some nonsense because an empty array doesn’t jive with the exception raised by your block. Useful. So if you don’t care what exception is raised, assert_raise isn’t gonna help you.

Also, assert_raise doesn’t let you specify what kind of exception message you’re expecting. I actually don’t mind that a given assertion should verify exactly one thing. On the other hand I have to jump through hoops to capture the exception if I want to assert on the error message.

A shoulda macro to the rescue

As usual, Shoulda is there to help us keep our test code DRY and intuitive. I’ve concocted a useful macro called should_raise. Here’s the gist:

It must be called with the block you expect to raise an exception, of course. You can also specify two optional arguments, the exception type and the message.

:kind_of or :instance_of

If not specified, an assertion is made that an exception was raised, but with no restriction on the type of the exception.

If you specify :kind_of, the assertion will be that much more precise. It will check that the exception raised was of the type specified, or a descendant.

If you specify :instance_of, the assertion is now that the exception raised was exactly of the type specified.

A shorthand is also available, where the type of the exception is supplied directly, like should_raise(LoadError), in which case the assertion is the same as with :instance_of.

In all of these cases, exactly one assertion is generated, whether or not :type is specified.

:message

If :message is specified, a second assertion will be added in order to make sure the error message matches the parameter. This can either be a string or a regex. The assertion is simply an assert_match.

If not specified, no assertion is generated for the message.

Examples

should_raise do
  require "more vespene gas"
end
# 1 assertion

should_raise(LoadError) do
  require "more vespene gas"
end
# 1 more restrictive assertion

should_raise(:instance_of => LoadError) do
  require "more vespene gas"
end
# 1 assertion, the same as should_raise(LoadError)

should_raise(:kind_of => ScriptError) do
  require "more vespene gas"
end
# 1 assertion, slightly less strict than with :instance_of (note: LoadError < ScriptError)

should_raise(:message => "no such file to load") do
  require "more vespene gas"
end
# 2 assertions

should_raise(:message => /vespene/) do
  require "more vespene gas"
end
# 2 assertions

should_raise(LoadError, :message => "such file to load") do
  require "more vespene gas"
end
# 2 assertions

should_raise(:kind_of => LoadError, :message => "file to load") do
  require "more vespene gas"
end
# 2 assertions

should_raise(:instance_of => LoadError, :message => "to load") do
  require "more vespene gas"
end
# 2 assertions

Conclusion

As you can tell, I’m eagerly awaiting Starcraft II.

No, I meant: check out the code on gist 20019. I’ve included a reasonable suite of unit tests in a comment at the end.

Feel free to use it in any way you like. Just make sure you don’t sue me.