2009-03-03 18 views
16

Pochodzę z tła C# i właśnie zacząłem programować w Ruby. Chodzi o to, że muszę wiedzieć, w jaki sposób mogę organizować wydarzenia na moich zajęciach, aby umożliwić różnym obserwatorom, gdy coś musi się wydarzyć.Jak robić wydarzenia w Ruby?

Problem polega na tym, że książki na temat Rubiego nie wspominają o wydarzeniach, nie wspominając o przykładach. Czy ktoś może mi pomóc?

Odpowiedz

4

Próbowałem napisać bibliotekę GUI w Ruby z małą literą C, a przede wszystkim z Ruby. Skończyło się tak powoli, że zrezygnowałem i nigdy go nie opublikowałem. Ale napisałem dla niego system zdarzeń, który starałem się ułatwić niż C#. Przepisałem go kilka razy, aby ułatwić jego użycie. Mam nadzieję, że jest to nieco pomocne.

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

Nie jestem pewien co masz na myśli, ale prawdopodobnie można używać wyjątków w swoich klasach i podnieść je na pewnych „wydarzeń”. Jeśli potrzebujesz zdarzenia do tworzenia GUI, większość frameworków GUI definiuje własny styl obsługi zdarzeń.

Mam nadzieję, że to nieco odpowiada na twoje pytanie.

+0

chcę użyć Wzorzec Obserwator projektu. – Ash

21

Na to pytanie już udzielono odpowiedzi, ale w bibliotece standardowej jest wbudowane observer, jeśli chcesz nadać temu wygląd. Używałem go w przeszłości do małego projektu gry i działa bardzo dobrze.

3

Niezwykle prosty słuchacz muzyki Ruby. Nie jest to dokładnie zamiennik zdarzeń .NET, ale ten jest wyjątkowo prostym przykładem bardzo prostego słuchacza.

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 

Sposób użycia:

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 

wyjściowa:

The cow said 'moooo!' 
+1

Ta odpowiedź jest najbardziej rubinowa. Opiera się ona na dynamicznej naturze języka, a nie na emulowaniu silnie napisanego, jest łatwa w obsłudze i zrozumieniu oraz obsługuje multiemisję. –

1

Ujawnienie: Jestem opiekunem event_aggregator gem

W zależności od tego, jak chcesz zgłosić się do problem, który można potencjalnie wykorzystać agregator zdarzeń ator. W ten sposób możesz publikować wiadomości określonego typu, a następnie słuchać obiektów, które chcesz otrzymywać. Może to w niektórych przypadkach być lepsze niż normalne zdarzenia, ponieważ uzyskujesz bardzo luźne połączenie między swoimi obiektami. Producent zdarzeń i odbiornik nie muszą udostępniać odniesienia do drugiego.

Istnieje klejnot, który pomaga w tym o nazwie event_aggregator. Dzięki niemu można wykonać następujące czynności:

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

Należy pamiętać, że jest domyślnie asynchroniczny, więc jeśli tylko wykonać ten skrypt skrypt może wyjść przed zapadają wydarzenia. Aby wyłączyć async wykorzystania Zachowanie:

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

Mam nadzieję, że może to być pomocne w przypadku