Ruby Language
Paso de mensajes
Buscar..
Introducción
En el diseño orientado a objetos, los objetos reciben mensajes y responden a ellos. En Ruby, enviar un mensaje está llamando a un método y el resultado de ese método es la respuesta.
En Ruby el paso de mensajes es dinámico. Cuando llega un mensaje en lugar de saber exactamente cómo responder, Ruby usa un conjunto predefinido de reglas para encontrar un método que pueda responderle. Podemos usar estas reglas para interrumpir y responder el mensaje, enviarlo a otro objeto o modificarlo entre otras acciones.
Cada vez que un objeto recibe un mensaje Ruby comprueba:
- Si este objeto tiene una clase singleton y puede responder a este mensaje.
- Busca la clase de este objeto y luego la cadena de ancestros de la clase.
- Uno por uno comprueba si un método está disponible en este antepasado y sube la cadena.
Mensaje que pasa a través de la cadena de herencia
class Example
def example_method
:example
end
def subexample_method
:example
end
def not_missed_method
:example
end
def method_missing name
return :example if name == :missing_example_method
return :example if name == :missing_subexample_method
return :subexample if name == :not_missed_method
super
end
end
class SubExample < Example
def subexample_method
:subexample
end
def method_missing name
return :subexample if name == :missing_subexample_method
return :subexample if name == :not_missed_method
super
end
end
s = Subexample.new
Para encontrar un método adecuado para el SubExample#subexample_method
Ruby primero mira la cadena de antepasados del SubExample
SubExample.ancestors # => [SubExample, Example, Object, Kernel, BasicObject]
Se inicia desde el SubExample
. Si enviamos el mensaje subexample_method
, Ruby elige el que está disponible, un SubExample e ignora el Example#subexample_method
.
s.subexample_method # => :subexample
Después del SubExample
verifica Example
. Si enviamos example_method
Ruby verifica si SubExample
puede responderle o no, y como no puede, Ruby sube la cadena y mira en Example
.
s.example_method # => :example
Después de que Ruby comprueba todos los métodos definidos, ejecuta method_missing
para ver si puede responder o no. Si enviamos missing_subexample_method
Ruby no podrá encontrar un método definido en el SubExample
por lo que se mueve hacia el Example
. Tampoco puede encontrar un método definido en Example
o cualquier otra clase superior en cadena. Ruby comienza de nuevo y ejecuta method_missing
. method_missing
of SubExample
puede responder a missing_subexample_method
.
s.missing_subexample_method # => :subexample
Sin embargo, si se define un método, Ruby usa una versión definida aunque sea más alta en la cadena. Por ejemplo, si enviamos not_missed_method
a pesar de que method_missing
de SubExample
puede responder, Ruby aparece en SubExample
porque no tiene un método definido con ese nombre y busca en Example
que tiene uno.
s.not_missed_method # => :example
Mensaje que pasa a través de la composición del módulo
Ruby se mueve hacia arriba en la cadena de antepasados de un objeto. Esta cadena puede contener tanto módulos como clases. Las mismas reglas sobre el ascenso de la cadena también se aplican a los módulos.
class Example
end
module Prepended
def initialize *args
return super :default if args.empty?
super
end
end
module FirstIncluded
def foo
:first
end
end
module SecondIncluded
def foo
:second
end
end
class SubExample < Example
prepend Prepended
include FirstIncluded
include SecondIncluded
def initialize data = :subexample
puts data
end
end
SubExample.ancestors # => [Prepended, SubExample, SecondIncluded, FirstIncluded, Example, Object, Kernel, BasicObject]
s = SubExample.new # => :default
s.foo # => :second
Mensajes de interrupcion
Hay dos formas de interrumpir los mensajes.
- Use
method_missing
para interrumpir cualquier mensaje no definido. - Defina un método en medio de una cadena para interceptar el mensaje
Después de interrumpir los mensajes, es posible:
- Responder a ellos.
- Envíalos a otro lugar.
- Modificar el mensaje o su resultado.
Interrupción a través de method_missing
y respuesta al mensaje:
class Example
def foo
@foo
end
def method_missing name, data
return super unless name.to_s =~ /=$/
name = name.to_s.sub(/=$/, "")
instance_variable_set "@#{name}", data
end
end
e = Example.new
e.foo = :foo
e.foo # => :foo
Interceptando mensaje y modificándolo:
class Example
def initialize title, body
end
end
class SubExample < Example
end
Ahora imaginemos que nuestros datos son "título: cuerpo" y tenemos que dividirlos antes de llamar a Example
. Podemos definir initialize
en el SubExample
.
class SubExample < Example
def initialize raw_data
processed_data = raw_data.split ":"
super processed_data[0], processed_data[1]
end
end
Interceptando mensaje y enviándolo a otro objeto:
class ObscureLogicProcessor
def process data
:ok
end
end
class NormalLogicProcessor
def process data
:not_ok
end
end
class WrapperProcessor < NormalLogicProcessor
def process data
return ObscureLogicProcessor.new.process data if data.obscure?
super
end
end