学んだことをなぐり書き


*(アスタリスク)引数

def m1 
  puts 'call m1'
end

m1
#引数が0個以外だとエラーになる
#m1(0)
#m1(:a, :b)

#引数を無視したい場合は * を付ける
#普通は余り使わないが稀に method_missing などで使用したりするっぽい。
def m2(*)
  puts 'call m2'
end

m2
m2(0)
m2(:a, :b)
call m1
call m2
call m2
call m2

メソッドの中にメソッドを定義した場合の挙動

結論:メソッドの中にメソッドを作りたい場合は 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
  • 参照
オープンクラス

メンバーのみ編集できます