soukouki’s diary

誰かの役に立つ記事をかけたらいいなあ

メタプログラミング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

www.oreilly.co.jp


rdefsってソースから正規表現で情報を集めてるのか。