Ruby

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

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"