The Journey to Ruby 1.9

Nick Quaranto

Thanks to some late-night hacking at Boston.rb and plenty of awesome contributors, our gems are now Ruby 1.9 compatible. If you’re wondering what’s different in the newest version of Ruby, check out this great list from Eigenclass.org. Here’s some tips and tricks for those who want to upgrade their own Ruby install and have their gems to be compatible.

''

Installation

It’s dead easy to run Ruby 1.8.* and 1.9 together on the same machine. Check out how to install it on OS X here, or on Ubuntu here. If you’ve got a great blog post on how to install it on your favorite OS, drop a comment. The most important thing to remember is when you configure your install, add

--program-suffix=1.9

as a flag so all of the binaries end in 1.9. Also, don’t forget to grab the latest RubyGems version. Once you’ve got it downloaded and unpacked, install with:

sudo ruby1.9 setup.rb --format-executable

Make sure to tack on --format-executable whenever you do a gem1.9 install. This will setup executables with the 1.9 suffix, so for instance you can have cucumber and cucumber1.9 living harmoniously together.

Gotchas

Here’s a list of some of the roadbumps we ran into while upgrading our gems, and hopefully you can keep an eye out for them in your code as well.

Files with non-ASCII characters need an encoding set

The basic solution to this is adding # encoding: utf-8 to the top of your file. Ruby just won’t parse the file without it. Of course, if your file has a different encoding you’ll need use that instead.

lambda is strict about arity

% irb
irb(main):001:0> lambda { 2 + 2 }.call(13)
=> 4
% irb1.9
irb(main):002:0> lambda { 2 + 2 }.call(13)
ArgumentError: wrong number of arguments (1 for 0)
    from (irb):2:in `call'
    from (irb):2
    from /opt/local/bin/irb1.9:12:in `<main>'

We saw this in Paperclip and Factory Bot. The solution is to make sure you’re calling the lambda with the right amount of arguments, and checking #arity if you have to.

Block-local variables don’t leak scope

This has probably been mentioned on every Ruby 1.9 blog post in existence, but it’s still worth noting.

% irb
irb(main):001:0> item = 1
=> 1
irb(main):002:0> lambda { |item| }.call(2)
=> nil
irb(main):003:0> item
=> 2

% irb1.9
irb(main):001:0> item = 1
=> 1
irb(main):002:0> lambda { |item| }.call(2)
=> nil
irb(main):003:0> item
=> 1

Basically, just make sure that you’re naming things properly. Here’s where we fixed it.

#instance_methods, #methods, and their cousins now return symbols, not strings

% irb
irb(main):001:0> Object.methods[0..5]
=> ["private_class_method", "inspect", "name", "tap", "clone", "public_methods"]

% irb1.9
irb(main):001:0> Object.methods[0..5]
=> [:allocate, :new, :superclass, :freeze, :===, :==]
</pre>

The basic fix for this is to tack .map(&amp;:to_s) onto the call. This was happening in quite a few gems.

Array#to_s does more of an inspect instead of its behavior in 1.8

% irb
irb(main):001:0> ["test", nil].to_s
=> "test"

% irb1.9
irb(main):001:0> ["test", nil].to_s
=> "[\"test\", nil]"

We saw this in one of Paperclip’s tests. While the new behavior seems to make more sense, just watch out if old code depends on it.

The __FILE__ constant is an absolute path in tests

This example is a default jeweler generated gem with p __FILE__ in a test:

% rake
(in /Users/qrush/Dev/ruby/test-gem)
/opt/local/bin/ruby -I"lib:lib:test" "/opt/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
  "test/test-gem_test.rb"
Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
"./test/test-gem_test.rb"
.
Finished in 0.000371 seconds.

% rake1.9
(in /Users/qrush/Dev/ruby/test-gem)
Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler
/opt/local/bin/ruby1.9 -I"lib:lib:test" "/opt/local/lib/ruby1.9/gems/1.9.1/gems/rake-0.8.7/lib/rake/rake_test_loader.rb"
  "test/test-gem_test.rb"
Loaded suite /opt/local/lib/ruby1.9/gems/1.9.1/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
"/Users/qrush/Dev/ruby/test-gem/test/test-gem_test.rb"
.
Finished in 0.000728 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

The solution to this was to use File.expand_path to make sure that it worked on both versions. This happened in Paperclip’s tests as well.

''

Onward

We test all of our gems on 1.8.7 and 1.9.1 on our Integrity CI server as well, if you’re curious. As of today we’re not yet running full time on 1.9, but we hope to be there soon. The main reason we’re not is because some gems/plugins aren’t compatible, and some hosts don’t support it. We’ll post more articles in the future once we’re running our Rails projects on Ruby 1.9.