2008-10-29 7 views
11

Das Problem ist sehr einfach. Ein Objekt muss einige Ereignisse melden, die für Beobachter von Interesse sein könnten.Was entspricht .NET-Ereignissen in Ruby?

Als ich saß, um ein Design zu validieren, das ich jetzt in Ruby aufbaute, nur um es zu validieren. Ich finde mich selbst nicht sicher, wie ich die Objektereignisse implementieren soll. In .Net wäre dies ein One-Liner .. .Net macht auch Handler Methode Unterschrift Verifikation, etc. z.B.

// 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 

Gibt es ein EventDispatcher-Modul oder etwas, das ich vermisse, das ich an eine Ruby-Klasse anschließen kann? Hoffe auf eine Antwort, die mit Rubys Prinzip der geringsten Überraschung zusammenspielt. Ein Ereignis wäre der Name des Ereignisses plus eine Warteschlange von [observer, methodName] -Objekten, die aufgerufen werden müssen, wenn das Ereignis stattfindet.

Antwort

2
+1

Nicht ganz .. Dies scheint eine Implementierung des Observer Design Pattern zu sein. Was ich brauche, ist, dass das Boiler-Objekt mehrere Ereignisse wie WaterExhausted, Started usw. und Clients, die ihre Handler abonniert haben, freigibt (nicht auf eine einzige "Update" -Methode beschränkt). – Gishu

8

Erstens gibt es in Ruby keine Methodensignaturen. Der nächste würde die Funktionsfähigkeit der Funktion überprüfen. Duck Typing erfordert anders zu denken (leicht).

Das Observable-Modul ist ein Start, aber wenn Sie mehrere Ereignisse aus einer einzelnen Klasse haben müssen, ist es möglicherweise nicht genug.

Dies ist eine schnelle Skizze. Es unterstützt Methoden und Blöcke. Ändern Sie sie nach Bedarf, um sie an Ihren Code, Threading-Ansatz usw. anzupassen. Beispielsweise könnten Sie method_missing verwenden, um den Ereignisnamen im Methodennamen zu haben, anstatt ihn als Parameter zu verwenden.

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

Ich würde Echo, dass es keine Sprache-Ebene Analog in Ruby zu .NET Ereignisse gibt. Die Art, wie Schienen damit umgehen, ist mit ActiveSupport::Callbacks (es gibt ein Beispiel auf dieser Seite).

+0

Vermutlich, weil Ruby eine Sprache ist, kein Framework? –

+0

wenn ich das verstehe..Callbacks wie in Rails sind eher wie Hooks .. wo müssen Sie von einer Klasse zu Plug-in-Verhalten an einem bestimmten Punkt abzuleiten. nicht ganz Ereignisse .. die nicht den "Beobachter" und das "Observable" koppeln – Gishu

6

Warum schreiben Sie nicht Ihre eigene Event-Klasse? Etwas wie

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 

Dies ist nur ein minimales Beispiel, kann aber in jeder Hinsicht erweitert werden. Fügen Sie Parameter wie Absender und eventArgs in .Net, oder was auch immer Sie mögen hinzu ;-)

0

Ich schrieb ein Juwel nur dafür, weil ich genau das gleiche Problem hatte. Versuchen Sie folgendes:

gem install ruby_events 

Folgen Sie den Anweisungen auf http://github.com/nathankleyn/ruby_events, aber auf den Punkt:

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

Eine kurze Anmerkung zu diesem Thema. Ich schlage vor, Sie schauen EventMachine

Es ist eine andere eine die gleiche Idee aussehen. Es implementiert ein ereignisgesteuertes Paradigma, sodass Sie eine Stufe über der Entsprechung für .NET-Ereignisse liegen und das Modul EventMachine als CLR-Ereignisbehandlungsroutine betrachten.

Ruby folgt einem Smalltalk-Verarbeitungsmodell, bei dem jeder Aufruf einer Methode eine Nachricht (wie auch ein Event) an das Objekt sendet (siehe Methode Send()). EventMachine gibt Ihnen eine Singlethread-Scheibe auf den Ereignissen.Sie können etwas wie Rack verwenden, um Threads oder Worker zu behandeln.

1

Werfen Sie einen Blick in die verschiedenen ruby state machine libraries. Sie beabsichtigen, ein großes Problem zu lösen, nicht nur Ereignisse, sondern können Ihnen eine Lösung bieten.

Ich habe die state_machine Juwel mit Erfolg verwendet, , die events enthält.

0

Ich bin ein Noob aber Ruby scheint wirklich mächtig.

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

Ich habe ein Juwel geschaffen tun genau das, was Sie wollen, und überraschend genannt event_dispatcher wie Sie bereits erwähnt: Sie können C# Stil Ereignisse selbst, wie diese umzusetzen. Ich hoffe, es wird jemandem helfen: event_dispatcher