Ruby 3.2 アドベントカレンダーの9日目の記事です。
Class
Class#attached_object 追加
Feature #12084: Class#instance
- Ruby master - Ruby Issue Tracking System
Ruby では、オブジェクトのクラスには存在しないメソッドをオブジェクト専用に定義できる。
class Hoge end hoge1 = Hoge.new hoge2 = Hoge.new def hoge1.fuga end hoge1.fuga hoge2.fuga #=> undefined method `fuga' for #<Hoge:0x00007f4174ff6830> (NoMethodError)
上の例では Hoge
クラスのオブジェクト hoge1
には fuga
があるけど、同じ Hoge
クラスのオブジェクトの hoge2
には fuga
が無いのでエラーとなる。
普通はメソッドはクラスに定義されるものなんだけど、fuga
はhoge1
オブジェクトに直接定義されてるように見える。
けど、実は内部的には各オブジェクトには無名のクラスがあってそこにメソッドが定義されてる。
そのクラスを特異クラスといって、singleton_class
で取り出すことができる。
hoge1_s_c = hoge1.singleton_class #=> #<Class:#<Hoge:0x00007ff418c969e0>> hoge2_s_c = hoge2.singleton_class #=> #<Class:#<Hoge:0x00007ff418c96968>>
名前がついてなのでわかりにくいけど、hoge1_s_c
と hoge2_s_c
の値が違うので別のものだとわかる。
表示結果から Hoge
クラスのオブジェクトについてる特異クラスであることがわかる。
そのクラスに定義されてるメソッドの中に fuga
があるのがわかる。
hoge1_s_c.instance_methods.include?(:fuga) #=> true hoge2_s_c.instance_methods.include?(:fuga) #=> false
で、Ruby 3.1 まではこの特異クラスに対応するオブジェクトは Ruby 的に綺麗に取り出す方法がなかった。
Ruby 3.2 では attached_object
というメソッドで取り出すことができるようになった。
hoge1_s_c.attached_object #=> #<Hoge:0x00007ff418c969e0> hoge1_s_c.attached_object == hoge1 #=> true hoge2_s_c.attached_object #=> #<Hoge:0x00007ff418c96968> hoge2_s_c.attached_object == hoge2 #=> true
使いどころは自分はよくわかってない。
なお、特異クラスではないクラスで attached_object
を呼ぶとエラーになる。
Hoge.attached_object #=> `Hoge' is not a singleton class (TypeError)