結論:メソッドの中にメソッドを作りたい場合は lambda を使うべし。
# ruby ではメソッドのスコープ単位がクラス(モジュール)単位なので # メソッドの中にメソッドを定義してもそのメソッドに対してではなく # 現在のクラスに対してインスタンスメソッドが追加されることになる。 class A def a def b; puts 'call b'; end end end obj = A.new # def a を実行してないので def b はまだ認識していない p A.instance_methods(false) # a を呼んで初めて b を認識し追加されるが、これはクラス A のインスタンスメソッドのスコープにである。 obj.a p A.instance_methods(false) # よって b を呼ぶことができる。 obj.b class B def b; puts 'b'; end def a def b; puts 'b2'; end end end # 問題なのは既に def b を定義していた場合、メソッドの中にメソッドを定義したつもりが上書きされているという点である。 # よって、メソッドのなかにメソッドを作りたい場合は lambda を使うべき。 obj = B.new obj.b obj.a obj.b
["a"] ["b", "a"] call b b b2
# Object.send(メソッド名, 引数) # 文字列によって動的にメソッドを呼び出す class C def hoge str puts str end private def moke puts 'private method' end end obj = C.new obj.send(:hoge, 'hoge') def obj.send *args puts 'send' end # send が 使用済みの場合にそなえて __send__ という alias も用意されている。 obj.send(:hoge, 'hogehoge') obj.__send__(:hoge, 'hogehoge') # private なので呼びだせない # puts obj.moke # send は private メソッドも呼べる。 obj.__send__(:moke)
hoge send hogehoge private method
# Module.define_method(メソッド名) do |メソッドの引数| メソッドの内容 end # 動的にメソッドを定義できる。 class C %w{a b c}.each do |name| define_method name do say name, name, name end end def set name self.class.send(:define_method, name) do |two| say name, two, name end end def say one, two, three puts one.downcase + two.upcase + three.downcase end end obj = C.new %w{a b c}.each do |name| obj.send(name) end # まだ定義されてない。 #obj.aiueo obj.set 'aiueo' obj.aiueo 'kakiku'
aAa bBb cCc aiueoKAKIKUaiueo
# Kernel.method_missing(メソッド名, 引数) # 定義されていないメソッドを呼び出したときに呼ばれるメソッド class C def method_missing(name, *args) puts "#{name}: #{args}" end end puts '----- C ----' obj = C.new obj.aiueo :a, :b, :c # 通常の respond_to? ではゴーストメソッドを認識していない。 puts obj.respond_to?(:aiueo) class C2 def respond_to?(method) [:a, :b, :c].include?(method) || super end def method_missing(name, *args) return super unless [:a, :b, :c].include?(name) puts "#{name}: #{args}" end end puts '----- C2 ----' obj = C2.new #a, b, c以外の method_missing は super なのでエラー #obj.d obj.a obj.b 1, 2, 3 puts obj.respond_to?(:a) puts obj.respond_to?(:d)
----- C ---- aiueo: abc false ----- C2 ---- a: b: 123 true false
# 既存のライブラリ、またはプロジェクト内で既に沢山の所で使われているクラスC があるとする class C def say(str); puts str.upcase; end end # sayメソッドは型チェックしてないので String(というか upcase() メソッドがないオブジェクト) 以外だとエラーになる C.new.say('aiueo') # C.new.say(3) #=> NoMethodError # この say に型チェックの処理を追加して既存の say を使いたい場合 # オープンクラスでメソッドを上書きしてしまうと既存の say が呼べない。 # オープンクラスで上書きした場合は super も出来ない。こういう場合は # # 1.メソッドにエイリアスをつける # 2.新しいメソッドを定義する # 3.新しいメソッドから古いメソッドを呼び出せば良い # # これは「アラウンドエイリアス」と呼ばれる。 class C # alias を定義する場合のメソッド名はシンボルでもコロンのない素のメソッドでもいいが # 多くの Rubyist はシンボルを使う # alias はメソッドではなくキーワードなので間にカンマは不要 # alias と同じ動きをする Module#alias_method() もある alias :old_say :say def say(str) str.kind_of?(String) ? old_say(str) : puts("not String: #{str}") end end C.new.say('abcde') C.new.say(5)
AIUEO ABCDE not String: 5
- 参照
最新コメント