はじめに
『メタプログラミングRuby 第2版』という書籍を読んでいて、混乱した箇所があったため、備忘録としてまとめます。
この記事におけるバージョンは Ruby 3.3 です。
問題
p138ではModule#alias_method
の説明がされています。
Module#alias_method
は既存のメソッドにエイリアス(別名)をつけるメソッドです。
次のサンプルコードで説明されています。
class String
alias_method :real_length, :length
def length
real_length > 5 ? 'long' : 'short'
end
end
"War and Peace".length #=> "long"
"War and Peace".real_length #=> 13
ところがこのサンプルコードをIRBで実行すると次のエラーが発生しました。
irb(main):001* class String
irb(main):002* alias_method :real_length, :length
irb(main):003*
irb(main):004* def length
irb(main):005* real_length > 5 ? 'long' : 'short'
irb(main):006* end
irb(main):007> end
irb(main):008>
irb(main):009> "War and Peace".length
irb(main):010> "War and Peace".real_length
An error occurred when inspecting the object: #<TypeError: String can't be coerced into Integer>
Result of Kernel#inspect: #<Integer:0x000000000000001b>
=>
irb(main):011>
おかしいなと思い、今度はサンプルファイルを次のように修正して、ターミナルからファイルを実行してみます。
class String
alias_method :real_length, :length
def length
real_length > 5 ? 'long' : 'short'
end
end
# puts で出力する
puts "War and Peace".length
puts "War and Peace".real_length
するとこちらは狙い通りの挙動となりました。
$ ruby class_definitions/wrapper_around_alias.rb
long
13
$
つまり、IRBで実行するときだけエラーが発生してしまう状況です。
IRBでのみ呼び出されるメソッドに問題があるはずですが、特定に時間がかかりました。
理由
IRBでは、結果を表示する際にObject#inspect
メソッドが自動的に呼び出されます。
このときにprettyprint
が使用され、出力を整形します。
ですが、今回の例ではString#length
メソッドが上書きされているため、本来Integer
型を返すべき部分でString
型が返されている状況です。
その結果、以下の箇所でエラーが発生していました。
def text(obj, width=obj.length) # lengthを上書きしているため、width は String 型の値
if @buffer.empty?
@output << obj
@output_width += width # width は Integer 型を想定しているためエラーが発生
else
text = @buffer.last
unless Text === text
text = Text.new
@buffer << text
end
text.add(obj, width)
@buffer_width += width
break_outmost_groups
end
end
上記の@output_width
はInteger型を想定していますが、width
にはString型が格納されています。
これによりTypeError: String can't be coerced into Integer
が発生しました。
なお、この挙動はバグではなく、String#length
を上書きした結果生じる正常な動作です。
おわりに
手を動かしながら書籍を読み進めていたため、このエラーに気づきました。
「組み込みクラスのメソッドは安易に上書きすべきではない」というのを身をもって体験できてよかったです。
この記事に誤りがありましたら、コメントにてご指摘いただけますと幸いです。
参考資料