Errata: Porting Adhearsion to Ruby 1.9

There are lots of guides to be found on the internet detailing the changes between Ruby 1.8 and Ruby 1.9.  These guides range from all-encompassing to cheat-sheets.  This article will in no way attempt to exhaustively detail what it takes to port software as complex as Adhearsion to Ruby 1.9.  However it is a collection of the notes, challenges and solutions I found while undertaking that effort.

  1. Unit Tests

    Before you even get started, make sure your unit test code coverage is good.  This effort would likely have been impossible were it not for the fantastic unit tests that Jay Phillips wrote as he created Adhearsion.  Unit tests will allow you to quickly pinpoint trouble spots and, when you are done, you will have a much higher level of confidence that the work is correct.  It also helps prevent any Ruby 1.8-incompatible changes from sneaking in.

  2. String no longer is Enumerable

    In Ruby 1.8 the String class mixed in the Enumerable module.  This allowed you to do things like: "example string".grep(/example/). In Ruby 1.9 this will result in a NoMethodError exception:

    NoMethodError: undefined method `grep' for "example string":String

    One way to deal with this is to use the split method like “example string”.split(“\n”) to coerce the string to an array.  Since Array includes Enumerable you can then use grep() on it.

    Another example of this is converting a string to an array:

    var = "example" var.to_a # => ["example"] (Ruby 1.8)

    Under Ruby 1.8 this results in an array with a single element being the original string.  One workaround for this is to use syntax like:

    var = "example" [*var].compact # => ["example"] (Ruby 1.8 & 1.9)

    The call to compact preserves a side effect of the to_a: if var is nil then the array will be empty.  But using the new notation would end up with an array with a single element of nil, which the call to compact removes.

    There are several methods that programmers may have used on strings that are actually from the Enumerable module. You may want to take a look at the Enumerable module documentation to refresh your memory.

  3. Strings arrays return characters instead of numbers

    This one is well documented but it is worth mentioning here:

    "asdf"[1] # => 115 (Ruby 1.8)
    "asdf"[1] # => "s" (Ruby 1.9)

    This applies to the “?” sigil as well:

    ?a # => 97  (Ruby 1.8)
    ?a # => "a" (Ruby 1.9)

    The fix is to call the .ord method if you need the character code:

    ?a.ord  # => 97
    "a".ord # => 97
  4. Method and constant names are now reported as symbols

    This is a simple one: using methods to get a list of methods or class constants like Object#singleton_methods or Module#constants used to return an Array of Strings.  They now return an Array of Symbols.  An easy fix for this is to call map on the result like this:

    methods.map(&:to_sym)
  5. Object#tap replaces Object#returning

    If you are a user of ActiveSupport you may have seen warnings like this in your code after upgrading to 2.3.10 or higher:

    DEPRECATION WARNING: Kernel#returning has been deprecated in favor of Object#tap.

    While not strictly a Ruby 1.9 issue (Object#tap was added to Ruby 1.8.7 as well) it may be an issue for older Ruby installs, such as the 1.8.6 that shipped with Ubuntu 8.04 LTS or the 1.8.5 that shipped with CentOS 5.

    There’s an easy fix: monkeypatch Object:

    if !Object.respond_to?(:tap)
      class Object
        def tap
          yield self
          self
        end
      end
    end

    Otherwise, simply replace all calls to returning with calls to tap.

  6. Dynamically defined attr_accessors are private

    This one very well may be a bug and not intentional, but it exists nonetheless in Ruby 1.9.2-p136.  It is possible to alter objects after creation by sending the object :attr_accessor with a symbol representing the instance variable to expose.  In Ruby 1.8 this resulted in a pair of methods for reading and writing the instance variable.  In Ruby 1.9 the methods are created as private and therefor raise an exception when accessed.  Fixing it is as simple as sending :public afterward, though it does feel dirty:

    class Foo
      def initialize
        @var = "Find me!"
      end
    end
    x = Foo.new
    x.send(:attr_accessor, :var)
    x.var # => NoMethodError
    x.send(:public, :var, :var=)
    x.var # => "Find me!"
    x.var = "I found you"
  7. Proc objects must declare arguments when being used like a method

    This may be a more esoteric use, but it came up in Adhearsion due to some metaprogramming in our unit tests.  In Ruby 1.8 it was legal to call a block with arguments, even if that block did not declare them:

    code = Proc.new { puts "Hello World" }
    Object.send(:define_method, :some_method, &code)
    some_method(nil) # => ArgumentError

    Under Ruby 1.8, the nil argument would simply be ignored.  Under Ruby 1.9 you would get a somewhat cryptic exception: ArgumentError: wrong number of arguments (1 for 0)

    The fix is to allow arguments to be passed to your block:

    code = Proc.new { |*args| puts "Hello World" } 

If there is one theme that dominated the above paragraphs it would be that Ruby 1.9 is generally much stricter about syntax and form than Ruby 1.8.  Some of the conveniences you may have leaned on no longer exist.  While this may create work for people as they upgrade, it is probably a good thing overall for the language.

 

Subscribe to our mailing list

* indicates required
I want to read about...
Email Format

What do you think?