Podemos usar define_method dentro de missing_method para crear
el método fantasma. define_method define un método de instancia en el objeto receptor.
La sintáxis es:
define_method(symbol, method) => new_method
define_method(symbol) { block } => proc
El parámetro method puede ser un Proc o un Method.
Si se usa un bloque como parámetro, es usado como cuerpo del método.
El bloque es evaluado utilizando instance_eval.
Puesto que define_method es privado y de clase, no podemos llamarlo
directamente dentro de method_missing. Al ser privado no podemos llamarlo
explicitamente en la clase. Dentro de method_missing la variable self
refiere al objeto, y define_method no es un método de instancia.
Una solución es llamarlo via Kernel.send.
MacBook-Air-de-casiano:rubytesting casiano$ cat -n chamcham3.rb
1 class A
2 def method_missing(n, *a)
3 super unless %{aa bb cc dd}.include? n.to_s
4
5 m = Kernel.send :define_method, n do
6 puts "Inside #{n}"
7 end
8 m[]
9 puts "Inside method_missing, building #{n}"
10 end
11 end
12
13 a = A.new()
14 a.aa()
15 a.aa()
16 a.aa()
17 a.ee()
MacBook-Air-de-casiano:rubytesting casiano$ ruby chamcham3.rb
Inside aa
Inside method_missing, building aa
Inside aa
Inside aa
chamcham3.rb:3:in `method_missing': undefined method `ee' for #<A:0x10513dcf0> (NoMethodError)
from chamcham3.rb:17
Otra forma de hacerlo es usar class_eval:
MacBook-Air-de-casiano:rubytesting casiano$ cat -n chamcham5.rb
1 class A
2
3 def method_missing(n, *a)
4 super unless %{aa bb cc dd}.include? n.to_s
5 A.class_eval {
6 m = define_method(n) do
7 puts "Inside #{n} #{self.class.ancestors.inspect}"
8 end
9 m[]
10 }
11 puts "Inside method_missing, building #{n}"
12 end
13 end
14
15 a = A.new()
16 a.aa()
17 a.aa()
18 a.aa()
MacBook-Air-de-casiano:rubytesting casiano$ ruby chamcham5.rb Inside aa [Class, Module, Object, Kernel] Inside method_missing, building aa Inside aa [A, Object, Kernel] Inside aa [A, Object, Kernel]
O bien
llamar a un método de clase desde el cual se llame
a defined_method:
MacBook-Air-de-casiano:rubytesting casiano$ cat -n chamcham4.rb
1 class A
2 def self.build_method(n)
3 m = define_method(n) do
4 puts "Inside #{n} #{self.class.ancestors.inspect}"
5 end
6 m[]
7 end
8
9 def method_missing(n, *a)
10 super unless %{aa bb cc dd}.include? n.to_s
11 self.class.build_method(n)
12 puts "Inside method_missing, building #{n}"
13 end
14 end
15
16 a = A.new()
17 a.aa()
18 a.aa()
19 a.aa()
MacBook-Air-de-casiano:rubytesting casiano$ ruby chamcham4.rb Inside aa [Class, Module, Object, Kernel] Inside method_missing, building aa Inside aa [A, Object, Kernel] Inside aa [A, Object, Kernel]
Casiano Rodriguez León 2015-01-07