From the very beginning of the project, we’ve developed Wallaby and its stack in Ruby 1.8 and not paid much attention to Ruby 1.9. We had done so for a few reasons, but primary among these is that we had internal Ruby 1.8 experience and that we needed to support Wallaby on 1.8.6 for the foreseeable future (and thus wouldn’t be writing code depending on 1.9 language or library features). All of that changed, of course, once we began packaging Wallaby for Fedora. Because Fedora 17 defaults to Ruby 1.9.3, we needed to port the code to work with both Ruby 1.9.3 and Ruby 1.8.6. Most major Ruby projects work with 1.9 these days, but I suspect a lot of code that is intended to be deployed on Ruby 1.8 is in the same boat we were. In this post, we’ll cover some of the pitfalls we ran into while boldly bringing our code base into 2009; we’ve kept an eye towards solutions that will work in 1.8 and 1.9, since we will be continuing to support Wallaby on Ruby 1.8.6.
Standard library interface changes
In Ruby 1.8, Module#instance_methods
returned an Array
of String
s; in Ruby 1.9, it returns an Array
of Symbol
s. The return type of Module#constants
has also changed similarly. Module#method_defined?
and Module#const_defined?
still accept String
or Symbol
parameters, though; when checking for the existence of a method or constant, prefer these. If you need to iterate through or grep for a substring in an Array
of method or constant names, map each value to the result of calling to_s
on itself first.
The sort of String
returned by the to_s
method on collection classes has changed in Ruby 1.9; it is now similar to the return value of the inspect
method. So, in Ruby 1.8:
= %w{foo bar blah}
ls # => ["foo", "bar", "blah"]
<< %w{fred barney}
ls # => ["foo", "bar", "blah", ["fred", "barney"]]
.to_s
ls# => "foobarblahfredbarney"
.inspect
ls# => "["foo", "bar", "blah", ["fred", "barney"]]"
Whereas in Ruby 1.9, the result is this:
= %w{foo bar blah}
ls # => ["foo", "bar", "blah"]
<< %w{fred barney}
ls # => ["foo", "bar", "blah", ["fred", "barney"]]
.to_s
ls# => "[\"foo\", \"bar\", \"blah\", [\"fred\", \"barney\"]]"
.inspect
ls# => "[\"foo\", \"bar\", \"blah\", [\"fred\", \"barney\"]]"
If you have code that depends on the old to_s
behavior (we did, but I intend to conceal the identity of the developer responsible in order to protect the guilty), you can approximate it in several backwards-compatible ways: if you’re only worried about Array
s that contain String
elements, the easiest thing to do is just to call ls.join("")
.
Semantic changes in the Ruby language
The big change that affected us — and will probably affect you, too, if you do a lot of metaprogramming or module trickery — involves block scoping. In some cases in our code, define_method
blocks that referred to free variables exhibited different behavior on Ruby 1.8 and Ruby 1.9. The solution in these cases is ugly but straightforward: use a Proc
object as you would use a let
in Scheme, like this:
= :something
free_var
# ...
Proc.new do |fv|
"my_method" do
define_method
fvend
end.call(free_var)
Text-encoding issues
A lot of people have written about Ruby 1.9’s support for multiple text encodings. If you’re using native extension libraries that create strings and haven’t been explicitly vetted for 1.9 compatibility, you’ll want to make sure that the Ruby String
objects are created with the appropriate encoding metadata. In our case, the QMF library was returning UTF-8 strings that had encoding type BINARY
in Ruby (i.e. raw bytes). Consider two String
objects with identical sequences of bytes; one is tagged BINARY
and the other is tagged UTF-8
: these will be indistinguishable if you print them to a terminal or write them to a file, but Ruby’s comparison operators will not find them identical. We submitted a patch to QMF to ensure that strings returned from QMF are either tagged as the default external encoding or as UTF-8 (if no external encoding is specified).
In conclusion
This isn’t an exhaustive list of all of the changes between Ruby 1.8 and Ruby 1.9, of course (see here for that), but it is a set of problems that folks developing networked services and infrastructure for these might need to worry about. The standard-library interface changes were pretty minor; the other issues were listed roughly in order of increasing frustration. The important news, of course, is that Wallaby and its dependencies now work with Ruby 1.9.3 and are thus readily available to Fedora 17 users. Go forth, install, and configure!