строгий метод to_str
. Из всех встроенных классов только String
и Exception
реализуют to_str
, и лишь String
, Regexp
и Marshal
вызывают его. Увидев сообщение TypeError: Failed to convert xyz into string
, можно смело сказать, что интерпретатор пытался вызвать to_str
и потерпел неудачу.
Вы можете реализовать метод to_str
и самостоятельно, например, если хотите, чтобы строку можно было конкатенировать с числом:
class Numeric
def to_str
to_s
end
end
label = 'Число ' + 9 # 'Число 9'
Аналогично обстоит дело и в отношении массивов. Для преобразования объекта в массив служит метод to_a
, а метод to_ary
вызывается, когда ожидается массив и ничего другого, например в случае множественного присваивания. Допустим, есть предложение такого вида:
а, b, с = x
Если x
— массив из трех элементов, оно будет работать ожидаемым образом. Но если это не массив, интерпретатор попытается вызвать метод to_ary
для преобразования в массив. В принципе это может быть даже синглетный метод (принадлежащий конкретному объекту). На само преобразование не налагается никаких ограничений, ниже приведен пример (нереалистичный), когда строка преобразуется в массив строк:
class String
def to_ary
return self.split('')
end
end
str = 'UFO'
a, b, с = str # ['U', 'F', 'O']
Метод inspect
реализует другое соглашение. Отладчики, утилиты типа irb
и метод отладочной печати p
вызывают inspect
, чтобы преобразовать объект к виду, пригодному для вывода на печать. Если вы хотите, чтобы во время отладки объект раскрывал свое внутреннее устройство, переопределите inspect
.
Есть и еще одна ситуация, когда желательно выполнять такие преобразования «за кулисами». Пользователь языка ожидает, что Fixnum
можно прибавить к Float
, а комплексное число Complex
разделить на рациональное. Но для проектировщика языка это проблема. Если метод +
класса Fixnum
получает аргумент типа Float
, то что он должен с ним делать? Он знает лишь, как складывать значения типа Fixnum
. Для решения проблемы в Ruby реализован механизм приведения типов coerce
.
Когда оператор +
(к примеру) получает аргумент, которого не понимает, он пытается привести вызывающий объект и аргумент к совместимым типам, а затем значения этих типов сложить. Общий принцип использования метода coerce
прямолинеен:
class MyNumberSystem
def +(other)
if other.kind_of?(MyNumberSystem)
result = some_calculation_between_self_and_other
MyNumberSystem.new(result)
else
n1, n2 = other.coerce(self)
n1 + n2
end
end
end
Метод coerce
возвращает массив из двух элементов: аргумент и вызывающий объект, приведенные к совместимым типам.
В примере выше мы полагались на то, что класс аргумента умеет как-то выполнять приведение. Будь мы законопослушными гражданами, реализовали бы приведение и в собственном классе, чтобы он мог работать с числами других видов. Для этого нужно знать, с какими типами мы можем работать напрямую, и приводить объект к одному из этих типов, когда возникает необходимость. Если мы сами не знаем, как это сделать, следует спросить у родителя:
def coerce(other)
if other.kind_of?(Float)
return other, self.to_f
elsif other.kind_of?(Integer)
return other, self.to_i
else
super
end
end
Конечно, чтобы этот пример работал, наш объект должен реализовывать методы to_i
и to_f
.
Метод coerce
можно использовать для реализации автоматического преобразования строк в числа, как это делается в языке Perl:
class String
def coerce(n)
if self['.']
[n, Float(self)]
else
[n, Integer(self)]
end
end
end
x = 1 + '23' # 24
y = 23 * '1.23' # 29.29
Впрочем, поступать так необязательно. Однако мы настоятельно рекомендуем реализовывать метод coerce
при разработке разного рода числовых классов.