2009-03-03 19 views
16

我來自C#背景,剛剛開始使用Ruby進行編程。問題是,我需要知道如何在類中引發事件,以便在需要發生事件時可以觸發各種觀察者。如何在Ruby中完成事件?

問題是我關於Ruby的書甚至沒有提到事件,更不用說提供示例了。有人能幫助我嗎?

回答

4

我嘗試在Ruby中用一個C編寫一個GUI庫,主要是Ruby。最終我放棄了,放棄了,從未放棄它。但是我爲它寫了一個事件系統,我試圖讓它比C#更容易。我重寫了它幾次,使它更容易使用。我希望這有些幫助。

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

我不確定你的意思,但是你可能會在你的類中使用異常,並在某些「事件」上提出異常。如果您需要GUI開發事件,那麼大多數GUI框架都會定義自己的事件處理風格。

希望這有些回答你的問題。

+0

我想使用Observer設計模式是有益的。 – Ash 2009-03-03 05:41:45

21

這個問題已經得到解答,但是如果你想要看一看標準庫中內置的observer。我過去曾經用它來做一個小型的遊戲項目,並且效果非常好。

3

非常簡單的Ruby監聽器。這不完全是.NET事件的替代品,但是這是一個非常簡單的監聽器的非常簡單的例子。

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 

要使用:

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 

輸出:

The cow said 'moooo!' 
+1

這個答案是最紅寶石般的。它依賴於語言的動態性,而不是模仿強類型語言,使用和理解起來很簡單,並且支持多播。 – 2013-11-22 15:46:38

1

披露:我是event_aggregator寶石

的維護者根據您想如何處理的問題你可能會使用事件聚合ATOR。通過這種方式,您可以發佈某種類型的消息,然後讓您的對象傾聽您希望他們接收的類型。在某些情況下,這可能會比正常事件更好,因爲您的對象之間會發生非常鬆散的耦合。事件生產者和監聽者不需要共享對另一個的引用。

有一個寶石,可以幫助你與這稱爲event_aggregator。有了它,你可以做到以下幾點:

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

要知道,這是預設非同步,所以如果你只執行這個腳本傳遞事件前的腳本可能會退出。要禁用異步行爲使用:

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

希望這能在你的情況

相關問題