メタプログラミングRuby2章を読んで調べたこと
はじめに
知人のエンジニアの方達とメタプログラミングRubyの輪読会をし、SmartHRさんで実施された問題を一緒に解いていく中で調べたことをまとめ。
対象リポジトリは下記。今回は02_object_model
に着手。
学んだメソッド
上記の問題のテストコードで下記メソッドを呼んでいて、見慣れなかったので調べました。(singleton_class
とclass_eval
)
# reading-metaprogramming-ruby/test/02_object_model/test_hierarchy.rb c4 = C4.new c4.singleton_class.class_eval do private def value=(x) @called_setter = true @value = x end def value @called_getter = true if defined?(@value) @value else nil end end
singleton_class
これはレシーバの特異クラスを返すメソッド。
レシーバの特異クラスがなければ作成する。 上記の例だとc4オブジェクトの特異クラスを作成して返す。
irbで動かすと下記のようになる。
irb(main):001:1* class C4 irb(main):002:0> end => nil irb(main):003:0> c4 = C4.new => #<C4:0x00007fcac4188be8> irb(main):004:0> c4.class => C4 irb(main):005:0> c4.singleton_class => #<Class:#<C4:0x00007fcac4188be8>> irb(main):006:0> c4.singleton_class.class => Class
c4オブジェクトのクラスはC4クラスだが、それとは別の特異クラスが作成されている。
class_eval
Module#class_eval (Ruby 3.0.0 リファレンスマニュアル)を見ると
モジュールのコンテキストで文字列 expr またはモジュール自身をブロックパラメータとするブロックを評価してその結果を返します。
とある。
渡されたブロックをクラス定義やモジュール定義と同じように扱える、とざっくり解釈した。
例えば先ほどのC4クラスのc4オブジェクトの特異クラスにメソッドを生やすことができる。
irb(main):001:1* class C4 irb(main):002:0> end => nil irb(main):003:0> c4 = C4.new => #<C4:0x00007fa8159ac5d0> irb(main):004:0> c4.singleton_class => #<Class:#<C4:0x00007fa8159ac5d0>> irb(main):005:1* c4.singleton_class.class_eval do irb(main):006:2* def hoge irb(main):007:2* p "インスタンスメソッド" irb(main):008:1* end irb(main):009:1* irb(main):010:2* def self.fuga irb(main):011:2* p "クラスメソッド" irb(main):012:1* end irb(main):013:0> end => :fuga irb(main):014:0> c4.hoge "インスタンスメソッド" => "インスタンスメソッド" irb(main):015:0> c4.singleton_class.fuga "クラスメソッド" => "クラスメソッド"
この場合、hogeメソッドはc4オブジェクトに後から独自のメソッドを生やしているので、c4オブジェクトの特異クラスのインスタンメソッドであり、c4オブジェクトの特異メソッドだと解釈した。
特異メソッド
特異メソッドについて理解が浅かったので調べた。
特異メソッドはある特定のオブジェクトだけで使うことができるメソッドのこと。 一般的な定義は下記のようになる。
irb(main):001:1* class C4 irb(main):002:0> end => nil irb(main):003:0> c4 = C4.new => #<C4:0x00007fa8159ac5d0> irb(main):004:1* def c4.hoge irb(main):005:1* "特異メソッド" irb(main):006:0> end => :hoge irb(main):007:0> c4.hoge => "特異メソッド"
先ほどの例のようにc4.singleton_class.class_eval
のブロック内でも同様に特異メソッドを生やすこともできる。