Quiero agregar un método singleton con un cierre a un objeto Ruby
-
16-09-2019 - |
Pregunta
Deseo agregar un método singleton a un objeto en particular. Deseo que cuando se llame por primera vez un método de instancia en un objeto, realice algo de trabajo y luego cree un método Singleton para dicho objeto del mismo nombre (que contiene el trabajo). En todas las llamadas posteriores en dicho objeto, el método Singleton borraría el método de instancia y se llamaría.
Sé cómo crear un método singleton, mi problema es que quiero que el método singleton creado para llamar a una lambda (l
en este caso). Def no crea un cierre, por lo que no puedo hacer referencia a la variable L (código a continuación) cuando el método se llama posteriormente (l.call()
se comenta en este ejemplo) Deseo saber cómo puedo crear un cierre al crear un método singleton en un objeto en particular. Cualquier ayuda sería apreciada. Gracias.
class Thing
end
t = Thing.new
t2 = Thing.new
Thing.instance_eval() do
def speak
puts "I speak for all Things, I am a class method"
end
end
Thing.class_eval() do
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
l = lambda {puts r}
instance_eval() do
def speak()
puts "This is the singleton method in the Thing object #{self}"
#l.call() # I want this to work! How?
end
end
end
end
Thing.speak()
t.speak()
t2.speak()
t.speak()
t2.speak()
Da los siguientes resultados cuando se ejecuta: (cambié '<' a '#' para que aparezcan en HTML)
Hablo por todas las cosas, soy un método de clase
Este es el método de instancia mencionado por el objeto de la cosa #Thing: 0x1d204>
Este es el método de instancia mencionado por el objeto de la cosa #thing: 0x1d1dc>
Este es el método Singleton en el objeto Thing #thing: 0x1d204>
Este es el método Singleton en el objeto Thing #thing: 0x1d1dc>
Solución
Puede definir un método con un bloque usando define_method
.
Ejemplo:
class Object
def eigenclass
class <<self; self end
end
end
a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String
Aquí hay una implementación de un define_singleton_method
método:
class Object
def define_singleton_method(name, &block)
eigenclass = class<<self; self end
eigenclass.class_eval {define_method name, block}
end
end
Otros consejos
Ahora que 1.9 está fuera, puede usar define_singleton_method:
jruby --1.9 -S irb
irb(main):019:0> fn = -> { length * 10 }
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):020:0> s.define_singleton_method :length_times_ten, fn
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):021:0> s
=> "a string"
irb(main):022:0> s.length_times_ten
=> 80
Bueno, una forma de hacerlo sería empacarlo en una variable de instancia:
(FYI puedes hacer class Thing
reabrir Thing
(es un poco más corto que usar #class_eval
, #instance_eval
para definir métodos dentro de un método).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
@l = lambda {puts r}
instance_eval do
def speak()
puts "This is the singleton method in the Thing object #{self}"
@l[]
end
end
end
end
Esto redefinirá #speak
, pero solo para ese caso de Thing
. Otros casos de Thing
todavía tendrá la definición original.
La alternativa es, como señaló Chuck, usar la clase Singleton (también conocida como MetaClass, también conocida como Eigenclass) asociada con la instancia. La clase Singleton es el objeto que almacena todos los métodos singleton asociados con un objeto. Puede obtener el contexto para la evaluación de la clase Singleton utilizando el divertido class <<object ; ... ; end
Sintaxis (similar al contexto dado por #class_eval
por clases normales).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
singleton_class = class <<self # open singleton class context for current instance
# in this context, self now refers to the singleton class itself
self
end
l = lambda {puts r}
singleton_class.class_eval do
# since we used #class_eval, local variables are still in scope
define_method(:speak) do
puts "This is the singleton method in the Thing object #{self}"
# since we used #define_method, local variables are still in scope
l[]
end
end
end
end