Edited at

prependを使ってクラスメソッドをoverrideする

More than 1 year has passed since last update.


Why

あるときだけクラスメソッドの挙動を変えたい。

(今回はCIで動かすときだけ挙動を変えるために使いました)


How

まぁ、ほとんど以下の通りなんですが。

How to prepend classmethods - Stack Overflow

class SomeClass

class << self
def some_classmethod
"Hello"
end
end
end

SomeClass.some_classmethod
#=> Hello

SomeClass.singleton_class.prepend Module.new {
def some_classmethod
super + " world"
end
}

SomeClass.some_classmethod
#=> "Hello world"

### 継承ツリーを確認
SomeClass.ancestors
#=> [SomeClass, Object, Kernel, BasicObject]
SomeClass.singleton_class.ancestors
#=> [#<Module:0x007fa1dd08acd8>, #<Class:SomeClass>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

#singleton_classprepend するのがポイント。

メタプログラミングRuby第2版」を読むとこの辺の理解が深まるらしいですよ(積読)


おまけ

今回は値を渡したくて以下のように書いたんですが、もっと良い方法があったら教えてください。

# SomeClass の定義は上記の通り

user_name = "John"

SomeClass.singleton_class.prepend Module.new { |mod|
# define_methodを使って外の変数を参照するメソッドを追加
mod.send(:define_method, :user_name) do
user_name
end

def some_classmethod
super + " #{user_name}"
end
}

SomeClass.some_classmethod
#=> "Hello John"