QA@IT

Railsで既存のクラスに動的にメソッドを追加するには?

4239 PV

現在rails3とruby 1.9.3で開発を行っているのですが、Stringクラスにメソッドを追加できなくてこまっています。

具体例をあげて説明させていただきます。
まずやりたいこととして、rubyに標準で入っているStringクラスにmy_methodというメソッドを追加したいです。

そこで、どんなクラスでも必ず一度は通過するであろうApplicationControllerに対して以下のように書きました。

class ApplicationController < ActionController::Base
  protect_from_forgery

  class String
    def my_method
      p "hello"
    end
  end
end

次にUserというコントローラを作り、そこに対して以下のように書きました。

class User < ApplicationController
     def index
           String.new.my_method#=>hello
           s = "a"
           s.my_method #=>NoMethodError
     end
end

するとs.my_methodはNoMethodErrorを返しました。
しかしながら、String.newで作ったものに対しては正常な結果が得られました。

なぜこのような結果になるのでしょうか?またこの様に既存のクラスにメソッドを追加するにはどうしたらいいですか?
ご回答お待ちしております。

回答

ApplicationControllerの中でStringクラスを定義したため、ApplicationController::Stringができてしまったようです。
以下のコードを実行してみると、新しくクラスが作られたこと、二つのクラスが別であることがわかるかと思います。

class User < ApplicationController
     def index
           String.new.my_method#=>hello
           p String.name #=> ApplicationController::String
           p String.new.size #=> 本来ならば0が返るが、NoMethodError

           s = "a"
           s.my_method #=>NoMethodError
           p s.class.name #=> String

           p String == s.class #=> false
     end
end

ApplicationControllerの外に書くだけでも動くようにはなりますが、config/initializersの中に適当なrubyファイルを作って記述すると起動時に自動的に実行してくれます。

編集 履歴 (3)
  • なるほど、インナークラスになっていたんですね。またinitializersというのがあったのですね、こちらを使おうと思います。ありがとうございました。 -

class や module の宣言/オープンを :: ではじめる方法を覚えておくと、役に立つことがあるかもしれません。今回の例では、以下でもよいはずです。

class ApplicationController < ActionController::Base

  class ::String
    def my_method
      p "hello"
    end
  end
編集 履歴 (0)
  • このような書き方があったのですね。参考にさせていただきます。 -
ウォッチ

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