NoMethodError というエラーがあるが、method_missingメソッドを使うと、これをコントロールできる。
試したのでメモ。
参考
NoMethodError を拾う
とりあえず、NoMethodError を起こしてみる。
NoMethodErrorを起こす| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | class MyMathattr_reader :val
 
 def initialize
 @val = 0
 end
 end
 
 math = MyMath.new
 
 math.plus
 
 
 
 | 
 
インスタンスに無いメソッドを呼べば、NoMethodErrorを起こせる。
NoMethodErrorを拾う| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | class MyMathattr_reader :val
 
 def initialize
 @val = 0
 end
 
 def method_missing(name)
 puts "#{name} => method_missingを呼び出した"
 end
 end
 
 math = MyMath.new
 
 math.plus
 
 
 
 math.plus(1)
 
 
 
 | 
 
method_missing を用意することで、クラスのインスタンスが持っていないメソッドが呼ばれた時に、method_missing が呼ばれるようになった。
method_missing で NoMethodError を詳細に拾う
method_missing を使って、呼び出し時の引数まで拾ってみると以下のようになる。
method_missing で NoMethodError で詳細に拾う| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | class MyMathattr_reader :val
 
 def initialize
 @val = 0
 end
 
 def method_missing(name, *args)
 puts "#{name} => method_missingを呼び出した"
 puts "args = #{args.inspect}"
 end
 end
 
 math = MyMath.new
 
 math.plus()
 
 
 
 math.plus(1, 2, 3, 4)
 
 
 
 | 
 
method_missing を使って処理を組み立てる
method_missing で実装されていないメソッドの呼び出しを拾うことができた。
メソッド名を処理して処理自体を組み立てることができる。
method_missing を使って処理を組み立てる| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 
 | class MyMathattr_reader :val
 
 def initialize
 @val = 0
 end
 
 def plus(number)
 @val += number
 end
 
 def sub(number)
 @val -= number
 end
 
 def method_missing(name)
 
 return super unless name.start_with?("calc_")
 
 
 options_str = name.to_s.gsub("calc_", "")
 
 
 options = options_str.split"_"
 
 index = 0
 while options.length > index do
 
 raise NoMethodError if options[index].nil? || options[index + 1].nil?
 
 
 raise NoMethodError if !["plus","sub"].include?(options[index])
 
 
 
 
 raise NoMethodError if options[index + 1].to_i == 0
 
 
 plus(options[index + 1].to_i) if options[index] == "plus"
 sub(options[index + 1].to_i) if options[index] == "sub"
 
 index += 2
 end
 
 self
 end
 
 def respond_to_missing?(name, *args)
 
 return true if name.start_with?("calc_")
 super
 end
 end
 
 math = MyMath.new
 
 puts math.respond_to?(:calc_calc_plus_1_plus_5_sub_2_plus_7)
 
 
 
 puts math.respond_to?(:hoge)
 
 
 
 puts math.calc_plus_1_plus_5_sub_2_plus_7.val
 
 
 
 
 | 
 
calc_plus_1_plus_5_sub_2_plus_7 というように、メソッド名で詳細なパラメータを与えて処理できる。
結局のところ処理したのは、0 + 1 + 5 - 2 + 7です。
method_missing のオーバーライドをするときには、対象のメソッド名が Object#respond_to? に対して true を返すようにドキュメントに書いてある。
対応としてrespond_to_missing? で呼び出されたメソッド名が、calc_ から始まるかだけをチェックする。
(本当なら method_missing に用意した処理同様のメソッド名のチェックをするのがいいのでしょうが)
method_missing はおもしろい、しかし多用するとチームで進めるとき混乱を生まないかやや不安が残る。
method_missing の使用事例として、proxy パターンと Builder パターンがある。
Qiita - Ruby によるデザインパターンまとめ
うまく付き合いたいものです。
ではでは。