QA@IT

Rubyのモジュール内でのクラス拡張について

3589 PV

たとえば、Objectを拡張するには以下のようにすると思います。

class Object
  def numeric?
    self.is_a? Numeric
  end
end

p 100.numeric? #=> true
p 'hoge'.numeric? #=> false

これをmodule内のすべてのObjectに対して適用したいのですが、書き方が分かりません。
やりたいのは、以下のような感じです。

module Hoge
  class Fuga
    def moga
      p 100.numeric? #=> true を希望(実際はNoMethodError)
    end
  end

  class Object
    def numeric?
      self.is_a? Numeric
    end
  end
end

考え方だけでも教えていただけると助かります。
よろしくお願いいたします。

回答

エラー自体は以下の様にすれば無くなると思いますが、
そうじゃなくてmoduleの外ではnumeric?メソッドを使わせたくないということです?

module Hoge
  class Fuga
    def moga
      p 100.numeric? #=> true を希望(実際はNoMethodError)
    end
  end

  class ::Object
    def numeric?
      self.is_a? Numeric
    end
  end
end

たとえばmoduleの外で

f = Hoge::Fuga.new
p f.moga        # こっちはtrueが出てほしい

p 200.numeric?  # こっちはエラーになってほしい

というようなことでしょうか(違ったらこっちは無視してください)。


追記

スコープ演算子ですね、親がないつまり最上位のオブジェクトであることを示します。
以下が参考になるかと思います。

http://docs.ruby-lang.org/ja/2.0.0/doc/symref.html#colon


「たとえばmoduleの外で」の方を実現する方法

結論から言えばあまり上手い手は考え付きませんでした。

要望とは違いますが例えば以下の様にすると、もともとメソッドが定義されていた場合の処理を書くことはできます。
これによって他の人が作った同名のメソッドがあった場合の制御を考えることができます。

module Hoge
  class Fuga
    def moga
      p 100.numeric? #=> true を希望(実際はNoMethodError)
    end
  end

  class ::Object
    if ::Object.method_defined? :numeric?
      alias :old_numeric? :numeric? # numeric? が既に定義があれば別名を作成
      def numeric?
        # self.is_a? Numeric
        old_numeric?                # 元々定義されていたメソッドを呼び出すなど。
      end
    else
      def numeric?
        self.is_a? Numeric
      end
    end
  end
end

加えてKernel.callerを使って呼び出し元ファイル名を取得すれば、
呼び出し元の制限もある程度実現できるかもしれません。
しかしわかりにくいコードになると思いますので、私だったら緊急回避なコード以外ではあまり使いたくないですね。

http://docs.ruby-lang.org/ja/1.9.3/class/Kernel.html#M_CALLER

そのmodule外から呼ばれたくない理由を整理して別の方法(上書きされないようなユニークな名前にするとか、別のprivateメソッドでラップするなど)も検討されてみてはどうでしょう。

こちらの記事も参考になるとは思います。
http://www.infoq.com/jp/articles/ruby-open-classes-monkeypatching

編集 履歴 (3)
  • ありがとうございます!まさしくこれです。1点お聞きしたいのですが、`::Object`のようなダブルコロン?はどのような意味なのでしょうか?名称だけでもお教えいただければと思います。 -
  • Hoge::Fugaの意味は分かるのですが、::Objectのように::から始まるものの意味が理解できません。。。検索しても、どのように検索すればよいか分からず。よろしくお願いいたします。 -
  • すみません!!
    > たとえばmoduleの外で
    こっちの方がお尋ねしたいことでした。。。こちらの解決策はあるのでしょうか??
    -
  • コメントの記法が分からず、見づらくなってしまいました。改めて、回答の「 > たとえばmoduleの外で」の方を知りたい、ということです。よろしくお願いしますm(_ _)m -
  • 回答に追記しました。 -
  • なるほど、スコープ演算子というのですね。その他の説明もありがとうございます! -
ウォッチ

この質問への回答やコメントをメールでお知らせします。