Ruby Integer(), Array(), et al -- what are they? Where do they come from?

ruby, ruby-on-rails

Solution

This is a good and difficult question. Let's answer the three parts.

First part

To find the definition, it is important to realize that the name of the method is "Array", etc., which can be quite counterintuitive, since methods are usually lowercase...

irb> method(:Array)
=> #<Method: Object(Kernel)#Array>

This tells you these are defined in Kernel, and thus available everywhere without requiring an explicit prefix.

Second part

`Array()`, `String()`,... are conversion methods. Calling `obj.to_a` will return an array, but will raise an `NoMethodError` if obj doesn't `respond_to? :to_a`. So the typical case when you'd prefer using `Array()`, `String()`, instead of `to_a` or `to_s` is when you are not positive an object responds to a given conversion method.

`String(obj)` will return `nil` if obj doesn't `respond_to? :to_s`. String(obj) will also check that the result of to_s is actually a string; it should be, but maybe an overly creative programmer decided to return something else?

Most other conversion methods act the same way, but `Array(obj)` is different. It will return `[obj]` if obj doesn't `respond_to? :to_a`. It will actually call `to_ary` (which is the implicit conversion operation, while `to_a` is the explicit one).

There is another important way to convert objects in 1.9 (and upcoming 1.8.8): `Array.try_convert(obj)`. This returns `nil` if the obj does not `respond_to? :to_ary`. It will not call the `to_a`. Although they are longer to type, you might prefer using them when writing very general code that might accept different types of objects and want to avoid converting a hash to an array by mistake, for example (since `Hash` has a `to_a` method but not `to_ary`). When your method requires an array-like object and you are willing to do an explicit conversion, then `obj.to_a` is fine. The typical use of `Array(obj)` would be in a method that accepts either a single `obj` to act on, or a list of objects (although typically this is written as `[*obj]`).

Last part

Hopefully, the answers to the first two parts give you your final answer...

You can use:

[Integer, String, Array].each {|klass| klass.try_convert(foo) }

or

[:Integer, :String, :Array].each{|method| send(method, obj)}

Problem

I've come across conversions of the form Array(value), String(value), and Integer(value) on occasion. It appears to me that these are just syntactic sugar for a call to the corresponding value.to_a, value.to_s, or value.to_i methods. So I'm wondering: - Where/how are these are defined? I can't find them in Object, Module, Class, etc - Are there any common scenarios for which it's preferable to use these rather than the corresponding/underlying to_X method? Could these be used in type-generic coercion? That is, can I do something along the lines of ``` [Integer, String, Array].each {|klass| klass.do_generic_coercion(foo) } ``` ? (...and no, I don't really want to do that; I know the type I want out, but I'm looking to avoid the case statement.)

Original source