先日 perl の SQL::Maker を ruby に移植したのだけれど、そこでは perl の EXPORT を ruby の include で代用したと書いた。

まぁ、それはそれでいいんだけど、

class Hoge
  include SQL::Maker::Helper
  def hoge
    sql_raw('*')
  end
end

とした場合に、Hoge.new.hoge の中で sql_raw が呼べるのはいいんだけど、 Hoge.new.sql_raw('*') のようにもメソッドが呼べてしまうのがなんか嫌だなぁと思った。 (正確には private にしているのでコレでは呼べないのだが、send(:sql_raw) とでもすれば呼べてしまう)。

なので、実際に使うファイルスコープだけに制限したいよね、と思って、ファイルスコープと言えば、ruby 2.1 には refinement があるのでそれを使って実現してみようと思った。

refinement は普通は例えば、

module モジュール
  refine クラス do
    def method1
    end
    def method2
    end
  end
end
class Hoge
  using モジュール # 「クラス」がこのファイルでのみ拡張される
end

のようにして、拡張したいクラスを refine の引数に指定して使うのだけど、今回は拡張したいクラスが

class Hoge #<= こいつ

みたいな任意のクラスなのでちょっと工夫する必要がある。モジュール定義を

module SQL::Maker::Helper
  def self.included(klass)
    refine klass do
      def method1
      end
      def method2
      end
    end
    # klass.__send__(:using, SQL::Maker::Helper) # これダメだった...メソッドの中で呼ぶなと怒られた
  end
end

こんなかんじにして、利用するクラスのほうで

class Hoge
  include SQL::Maker::Helper
  using SQL::Maker::Helper
  def hoge
    sql_raw('*')
  end
end

のようにしたら、

Hoge.new.hoge #=> 中では sql_raw が使える
Hoge.new.sql_raw('*') #=> undefined method !!!

となっていい感じになった。けど、include と using 二回呼んでるのがなぁ...

あったらうれしい

def self.included(klass) の using 版 def self.usinged(klass) (名前が微妙 ^^;) みたいな using をフックするメソッドがあると、 using 1回で実現できるようになるのでよさそう。

おわりに

なにか良いやり方があったら教えてください!^^