2009-03-03 13 views
16

Provengo da uno sfondo C# e ho appena iniziato a programmare in Ruby. Il fatto è che ho bisogno di sapere come posso aumentare gli eventi nelle mie classi in modo che vari osservatori possano essere attivati ​​quando le cose devono accadere.Come fare gli eventi in Ruby?

Il problema è che i libri che ho su Ruby non menzionano nemmeno gli eventi, per non parlare di fornire esempi. Qualcuno è in grado di aiutarmi?

risposta

4

Ho provato a scrivere una libreria GUI in Ruby con un po 'di C e principalmente Ruby. Alla fine è stato così lento che ho rinunciato e mai rilasciato. Ma ho scritto un sistema di eventi per questo che ho cercato di rendere più semplice di C#. L'ho riscritto un paio di volte per renderlo più facile da usare. Spero che sia alquanto utile.

class EventHandlerArray < Array 
    def add_handler(code=nil, &block) 
    if(code) 
     push(code) 
    else 
     push(block) 
    end 
    end 
    def add 
    raise "error" 
    end 
    def remove_handler(code) 
    delete(code) 
    end 
    def fire(e) 
    reverse_each { |handler| handler.call(e) } 
    end 
end 

# with this, you can do: 
# event.add_handler 
# event.remove_handler 
# event.fire (usually never used) 
# fire_event 
# when_event 
# You just need to call the events method and call super to initialize the events: 
# class MyControl 
# events :mouse_down, :mouse_up, 
#   :mouse_enter, :mouse_leave 
# def initialize 
#  super 
# end 
# def when_mouse_up(e) 
#  # do something 
# end 
# end 
# control = MyControl.new 
# control.mouse_down.add_handler { 
# puts "Mouse down" 
# } 
# As you can see, you can redefine when_event in a class to handle the event. 
# The handlers are called first, and then the when_event method if a handler didn't 
# set e.handled to true. If you need when_event to be called before the handlers, 
# override fire_event and call when_event before event.fire. This is what painting 
# does, for handlers should paint after the control. 
# class SubControl < MyControl 
# def when_mouse_down(e) 
#  super 
#  # do something 
# end 
# end 
def events(*symbols) 
    # NOTE: Module#method_added 

    # create a module and 'include' it 
    modName = name+"Events" 
    initStr = Array.new 
    readerStr = Array.new 
    methodsStr = Array.new 
    symbols.each { |sym| 
    name = sym.to_s 
    initStr << %Q{ 
     @#{name} = EventHandlerArray.new 
    } 
    readerStr << ":#{name}" 
    methodsStr << %Q{ 
     def fire_#{name}(e) 
     @#{name}.fire(e) 
     when_#{name}(e) if(!e.handled?) 
     end 
     def when_#{name}(e) 
     end 
    } 
    } 
    eval %Q{ 
    module #{modName} 
     def initialize(*args) 
     begin 
      super(*args) 
     rescue NoMethodError; end 
     #{initStr.join} 
     end 
     #{"attr_reader "+readerStr.join(', ')} 
     #{methodsStr.join} 
    end 
    include #{modName} 
    } 
end 

class Event 
    attr_writer :handled 
    def initialize(sender) 
    @sender = @sender 
    @handled = false 
    end 
    def handled?; @handled; end 
end 
-1

Non sono sicuro di cosa intendi esattamente, ma potresti probabilmente utilizzare eccezioni nelle tue classi e aumentarle in determinati "eventi". Se è necessario un evento per lo sviluppo della GUI, molti framework GUI definiscono il proprio stile di gestione degli eventi.

Spero che questo in qualche modo risponda alla tua domanda.

+0

voglio usare il pattern Observer Design. – Ash

21

Alla domanda è già stata data risposta, ma c'è uno observer incorporato direttamente nella libreria standard se si vuole dare un'occhiata. L'ho usato in passato per un piccolo progetto di gioco, e funziona molto bene.

3

Estremamente semplice ascoltatore di Ruby. Questo non è esattamente un sostituto per eventi .NET, ma questo è un esempio estremamente semplice di un ascoltatore molto semplice.

module Listenable 

    def listeners() @listeners ||= [] end 

    def add_listener(listener) 
     listeners << listener 
    end 

    def remove_listener(listener) 
     listeners.delete listener 
    end 

    def notify_listeners(event_name, *args) 
     listeners.each do |listener| 
      if listener.respond_to? event_name 
       listener.__send__ event_name, *args 
      end 
     end 
    end 

end 

Per utilizzare:

class CowListenable 
    include Listenable 

    def speak 
     notify_listeners :spoken, 'moooo!' 
    end 

end 

class CowListener 

    def initialize(cow_listenable) 
     cow_listenable.add_listener self 
    end 

    def spoken(message) 
     puts "The cow said '#{message}'" 
    end 

end 

cow_listenable = CowListenable.new 
CowListener.new(cow_listenable) 
cow_listenable.speak 

uscita:

The cow said 'moooo!' 
+1

Questa risposta sembra la più ruby-ish. Si basa sulla natura dinamica del linguaggio piuttosto che sull'emulazione di uno fortemente tipizzato, è semplice da usare e comprendere e supporta il multicast. –

1

Disclosure: Io sono il manutentore del event_aggregator gemma

A seconda di come si desidera avvicinarsi al problema che potresti potenzialmente utilizzare un aggregato di eventi ator. In questo modo puoi pubblicare messaggi di un certo tipo e poi far ascoltare i tuoi oggetti ai tipi che vuoi che vengano ricevuti. Questo può in certi casi essere migliore degli eventi normali perché si ottiene un accoppiamento molto lento tra gli oggetti. Il produttore e l'ascoltatore di eventi non devono condividere un riferimento all'altro.

C'è una gemma che ti aiuta con questo chiamato event_aggregator. Con esso è possibile effettuare le seguenti operazioni:

#!/usr/bin/ruby 

require "rubygems" 
require "event_aggregator" 

class Foo 
    include EventAggregator::Listener 
    def initialize() 
     message_type_register("MessageType1", lambda{|data| puts data }) 

     message_type_register("MessageType2", method(:handle_message)) 
    end 

    def handle_message(data) 
     puts data 
    end 

    def foo_unregister(*args) 
     message_type_unregister(*args) 
    end 
end 

class Bar 
    def cause_event 
     EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3]).publish 
    end 
    def cause_another_event 
     EventAggregator::Message.new("MessageType2", ["Some More Stuff",2,3]).publish 
    end 
end 

f = Foo.new 

b = Bar.new 
b.cause_event 
b.cause_another_event 
# => Some Stuff 
    2 
    3 
# => Some More Stuff 
    2 
    3 

essere consapevoli che è asincrono per impostazione predefinita, quindi se si esegue solo questo script lo script potrebbe uscire prima che siano passati degli eventi. Per disabilitare l'uso comportamento asincrono:

EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3], false).publish 
#The third parameter indicates async 

Speriamo che questo può essere utile nel vostro caso