2008-10-29 9 views
11

El problema es muy simple. Un objeto necesita notificar algunos eventos que pueden ser de interés para los observadores.¿Cuál es el equivalente de los eventos de .NET en Ruby?

Cuando me senté para validar un diseño que cociné ahora en Ruby solo para validarlo ... Me encuentro perplejo en cuanto a cómo implementar los eventos del objeto. En .Net, esto sería de una sola línea. .Net también hace la verificación de la firma del método del manejador, etc. p.ej.

// Object with events 
public delegate void HandlerSignature(int a); 
public event HandlerSignature MyEvent; 
public event HandlerSignature AnotherCriticalEvent; 

// Client 
MyObject.MyEvent += new HandlerSignature(MyHandlerMethod); // MyHandlerMethod has same signature as delegate 

¿Hay algún módulo de EventDispatcher o algo que me falta que pueda conectar a una clase de Ruby? Esperando una respuesta que coincida con el principio de menor sorpresa de Ruby. Un evento sería el nombre del evento más una cola de objetos [observer, methodName] que deben invocarse cuando se produce el evento.

Respuesta

2
+1

No del todo ... Esto parece ser una implementación del Patrón de Observer Design. Lo que necesito es que el objeto Boiler exponga varios eventos como WaterExhausted, Started, etc. y clientes que se suscriben con sus controladores (no está limitado a un solo método de actualización) – Gishu

8

En primer lugar, en Ruby no hay firmas de métodos. Lo más cercano sería verificar el valor de la función. Duck typing requiere pensar diferente (ligeramente).

El módulo Observable es un comienzo, pero si tiene un requisito para tener múltiples eventos de una sola clase, puede que no sea suficiente.

Este es un boceto rápido. Es compatible con métodos y bloques. Modifique según sea necesario para adaptar su código, enfoque de enhebrado, etc. Por ejemplo, podría usar method_missing para tener el nombre del evento en el nombre del método en lugar de tenerlo como parámetro.

class EventBase 
    def initialize 
     @listeners = Hash.new 
    end 

    def listen_event(name, *func, &p) 
     if p 
      (@listeners[name] ||= Array.new) << p 
     else 
      (@listeners[name] ||= Array.new) << func[0] 
     end 
    end 

    def ignore_event(name, func) 
     return if [email protected]_key?(name) 
     @listeners[name].delete_if { |o| o == func } 
    end 

    def trigger_event(name, *args) 
     return if [email protected]_key?(name) 
     @listeners[name].each { |f| f.call(*args) } 
    end 
end 


class MyClass < EventBase 
    def raise_event1(*args) 
     trigger_event(:event1, *args) 
    end 

    def raise_event2(*args) 
     trigger_event(:event2, *args) 
    end 
end 

class TestListener 
    def initialize(source) 
     source.listen_event(:event1, method(:event1_arrival)) 
     source.listen_event(:event2) do |*a| 
      puts "event 2 arrival, args #{a}" 
     end 
    end 

    def event1_arrival(*a) 
     puts "Event 1 arrived, args #{a}" 
    end 
end 

s = MyClass.new 
l = TestListener.new(s) 

s.raise_event1("here is event 1") 
s.raise_event2("here is event 2") 
2

Me gustaría repetir que no hay un análogo de nivel de idioma en Ruby para eventos .NET. La forma en que los rieles se trata es con ActiveSupport::Callbacks (hay un ejemplo en esa página).

+0

¿Presumiblemente porque Ruby es un lenguaje, no un framework? –

+0

si entiendo esto ... Las restauraciones como en Rails son más como ganchos ... donde tiene que derivar de un comportamiento de clase a plug-in en cierto punto. no del todo los eventos ... lo que no empareja al 'observador' y el 'observable' – Gishu

6

¿Por qué no escribir su propia clase de evento? Algo así como

class Event 
    def initialize 
    @handlers = Array.new 
    end 

    def fire 
    @handlers.each do |v| 
     v.call 
    end 
    end 

    def << handler 
    @handlers << handler 
    end 
end 

e = Event.new 

e << lambda { puts "hello" } 
e << lambda { puts "test" } 
e.fire 

Esto es solo una muestra mínima, pero se puede ampliar de cualquier manera. Agregue parámetros como remitente y eventArgs en .Net, o lo que quiera ;-)

0

Escribí una joya solo por esto porque tenía exactamente el mismo problema. Prueba esto:

gem install ruby_events 

Siga las instrucciones sobre como http://github.com/nathankleyn/ruby_events, pero en pocas palabras:

require 'rubygems' 
require 'ruby_events' 

class Example 
    def initialize 
    events.listen(:test_event) do |event_data| 
     puts 'Hai there!' 
     puts event_data 
    end 
    end 

    def call_me 
    events.fire(:test_event, 'My name is Mr Test Man!') 
    end 
end 

e = Example.new 
e.call_me # Fires the event, and our handler gets called! 
0

Una breve nota sobre esto. Yo sugiero que busque en EventMachine

Se trata de un aspecto diferente de una misma idea. Implementa un paradigma impulsado por eventos para que tenga un nivel superior al equivalente para eventos .Net y considere el módulo EventMachine como el controlador de eventos CLR.

También dando un paso atrás, Ruby sigue un modelo de procesamiento Smalltalk donde cualquier llamada a un método es un mensaje (como un Evento) enviado al objeto (ver el método Send()). EventMachine que le da es un segmento de un solo subproceso en los eventos.Puede usar algo como Rack para manejar hilos o trabajadores.

1

Eche un vistazo a los diversos ruby state machine libraries. Tienen la intención de resolver un problema de gran envergadura además de los simples eventos, pero pueden brindarle una solución.

He utilizado la gema state_machine con éxito, que incluye events.

0

Soy un novato pero Ruby parece realmente poderoso. Se puede implementar eventos C# del estilo mismo de esta manera:

module Observable 
    class Event 
     def initialize 
      @to_call = [] 
     end 
     def fire(*arguments) 
      @to_call.each { |proc| proc.call(*arguments) } 
     end 
     def call(proc) 
      @to_call << proc 
     end 
     def dont_call(proc) 
      @to_call.delete proc 
     end 
    end 
    def self.append_features(cls) 
     def cls.event(sym) 
      define_method(sym.to_s) do 
       variable_name = "@#{sym}" 
       if not instance_variable_defined? variable_name then 
        instance_variable_set variable_name, Event.new 
       end 
       instance_variable_get variable_name 
      end 
     end 
    end 
end 

# Example 

class Actor 
    include Observable 
    event :whenActed 
    def act 
     whenActed.fire("Johnny") # fire event whenActed with parameter Johnny 
    end 
end 

actor = Actor.new 

def apploud(whom) 
    print "Bravo #{whom}!\n" 
end 

applouder = method(:apploud) 

actor.whenActed.call applouder 

actor.act 
0

He creado una joya haciendo exactamente lo que quiere y sorprendentemente llamada event_dispatcher como usted ha mencionado. Espero que ayude a alguien: event_dispatcher

Cuestiones relacionadas