A Ruby Conversion Idiom

@CapnKernul writes:

[do you know of] a Ruby idiom for converting an object to a type if it isn’t already that type. For example, if you want to only store an attribute of type Foo, you could write an accessor method that would pass any non-Foo object to Foo’s constructor.

There is a de facto idiom among Ruby built-ins to define a method with the same name as a class for doing conversions to that class. My favorite is Kernel#Array:

Array("foo")                    # => ["foo"]
Array([1,2,3])                  # => [1, 2, 3]
Array(nil)                      # => []
Array({:a => 1, :b => 2})       # => [[:a, 1], [:b, 2]]

But there are others. Kernel#Integer provides a stricter form of integer conversion than does #to_i:

"42".to_i                       # => 42
Integer("42")                   # => 42
"30 seconds".to_i               # => 30
Integer("30 seconds")           # =>
# ~> -:4:in `Integer': invalid value for Integer(): "30 seconds" (ArgumentError)
# ~> from -:4:in `<main>'

And there are also Kernel#String, Kernel#Float, Kernel#Complex, and Kernel#Rational:

String(123)                     # => "123"
Float("123")                    # => 123.0
Rational(4,5)                   # => (4/5)
Complex(1,3)                    # => (1+3i)

You can also find this in standard libraries. The URI library defines an idempotent conversion method for URIs:

require 'uri'

u = URI("http://example.com")   # => #<URI::HTTP:0x000000030cac80 URL:http://example.com>
URI(u)                          # => #<URI::HTTP:0x000000030cac80 URL:http://example.com>

There’s also Pathname:

require 'pathname'

p = Pathname("~/.emacs.d")      # => #<Pathname:~/.emacs.d>
Pathname(p)                     # => #<Pathname:~/.emacs.d>

I like to extend this idiom into my own code for idempotent conversions into commonly-used value objects. I did this in Objects on Rails for TagList objects.

module Conversions
  private
  def TagList(value)
    return value if value.is_a?(TagList)
    TagList.new(value)
  end
end

The TagList method is shorter than calling TagList.new(...). And in addition, it’s safe to call on inputs which might or might not already be TagList objects.

Although they may look a little odd at first, capitalized conversion methods are a well-established Ruby idiom for methods which “do the right thing” to convery any reasonable input value into a desired class. And because unlike #to_x methods they are outside of the objects being converted, they incur none of the maintenance danger of a monkey-patch. I don’t define them for every class in my program; but for oft-used value object types they can be quite convenient.