Ruby でメソッドが呼び出された時にレシーバがあるかないかで処理を切り分ける

今日の Kaigi on Rails の kamipo さんの基調講演でそういう話がでてたのでやってみました。
要は User.where みたいに User. 付きか where だけで呼び出された場合で処理を切り分けたい的な話。

class User
  def self.where
    "レシーバなし"
  end

  def self.where_with_receiver
    "レシーバあり"
  end

  class <<self
    private :where

    # private メソッドでレシーバありで呼び出された場合に method_missing が呼ばれる
    def method_missing(name, *args)
      if name === :where
        where_with_receiver(*args)
      else
        super
      end
    end
  end
end

pp User.where
# => レシーバあり

pp User.instance_exec { where }
# => レシーバなし

# 動的呼び出しの場合
pp User.send(:where)
# => レシーバなし

pp User.public_send(:where)
# => レシーバあり

# NOTE: self. 付きは Ruby のバージョンによって変わる
pp User.instance_exec { self.where }
# Ruby 2.6 以前 => レシーバなし
# Ruby 2.7 以降 => レシーバなし

呼び出されたメソッドが private メソッドの場合『レシーバ付きだと method_missing が呼ばれる』のでそれで処理を切り分けるようにしています。
そもそもこの時に method_missing が呼ばれるのは期待する動作なのかどうか…。
Ruby 2.7 からは self. 付きでも private メソッドが呼び出せるようになっているのでその部分に差異があります。