When developing in Ruby, we may have to convert our own custom objects into one of the built-in system types.
Ruby has built-in support for conversion protocols. By that, it means Ruby allows an object of a class to have itself converted to an object of another class.
Ruby supports three main modes of type conversions: lenient
, strict
, numeric
. We will discuss the first two forms of conversions in this post.
When you call to_s
or to_i
on a receiver, Ruby looks for an equivalent representation
in the receiver. For instance, calling to_s
on any class which does not implement a custom to_s
method will return just the class name followed by the object ID as a string.
Sometimes, strict
conversions are necessary in order to use our custom objects interchangeably with the built-in system types - we need our custom objects to work together with the core system types.
This is when the strict conversion
functions from the standard library comes in handy.
Below is a list of these predefined system methods and their corresponding conversions:
Some of the Ruby built-in classes such as Array, Hash, Regexp, String support the try_convert
method call, which tries to transform the object passed as its argument into the corresponding type.
When try_convert
is invoked on an object, the type it is to be converted to looks for the associated method call in the list above in order to determine how the conversion is to occur.
Below is an example of transforming a custom class of type Alphabet into an array by implementing to_ary
:
1
2
3
4
5
6
7
8
9
class Alphabet
def to_a
('a'..'z').to_a
end
end
res = Array.try_convert(Alphabet.new) # => calls to_ary on class A
puts res.class # => Array
puts res # => ['a', 'b', 'c',...,'z']
This allows the use of the Alphabet class interchangeably when an array is expected.
However, we are not limited to just a single implementation.
Taking the example above, if we need to extend the conversions to the other classes, we can also add the other corresponding methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Alphabet
def alpha_range
('a'..'z')
end
# returns array of alphabets
def to_ary
alpha_range.to_a
end
# returns hash of alphabet as key and its position as value
def to_hash
('a'..'z').each_with_object({}){ |alpha, hsh|
hsh[alpha] = alpha_range.find_index(alpha)
}
end
# returns string of all alphabets concatenated
def to_str
self.to_ary.join(' ')
end
# calls to_str to return a Regexp
def to_regexp
Regexp.new(self)
end
# returns number of alphabets in range
def to_int
alpha_range.count
end
end
alphabets = Alphabet.new
Array.try_convert(alphabets) # => ['a', 'b', 'c',...,'z']
Hash.try_convert(alphabets) # => {"a"=>0, "b"=>1, "c"=>2, ..., "z" => 25}
String.try_convert(alphabets) # => 'a b c ... z'
Regexp.try_convert(alphabets) # => /a b c .... z/
Integer(alphabets) # => 26
Keep hacking and stay curious!