結論:メソッドの中にメソッドを作りたい場合は 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
- 参照

最新コメント