メタプログラミングRubyを読んで(4)
最終回です。
前回
eval
class AAA def get_binding binding end def aaa @aaa end end aaa = AAA.new aaa.aaa # => nil aaa.bbb # NameError: uninitialized class variable @@bbb in AAA aaa.get_binding # => #<Binding:0x00000002a16ab0> eval("@aaa=123",aaa.get_binding) aaa.aaa # => 123
evalの第二引数にはBindingが入り、任意のスコープで実行できる。
(この例はinstance_evalで十分だけど。)
evalは便利だが、コードインジェクションに弱い。
フックメソッド
class C1 def self.inherited(subclass) p "class #{subclass} < #{self}" end end class C1t < C1 end # => "class C1t < C1" module M1 def self.included(othermod) p "#{othermod}.include #{self}" end end module M1t include M1 end # => "M1t.include M1"
いくつか種類があり、 hatappo.hatenadiary.jp でまとめられています。
includedを使えば、
module M1 def aaa define_method "bbb" do p "call bbb" end end end class C1 class << self # カッコ悪い include M1 end aaa end C1.new.bbb # "call bbb"
C1のincludeの部分がカッコ悪かったのを、
module M2 def self.included(base) base.extend M2_ end module M2_ def aaa define_method "bbb" do p "call bbb" end end end end class C2 include M2 # 良くなった aaa end C2.new.bbb # "call bbb"
こんな風にできる。
ここのソースを全て順番に実行していくと、うまくいかない可能性があります。
1つずつ実行してください。
Pry version 0.10.4 on Ruby 2.3.0
rdefsってソースから正規表現で情報を集めてるのか。