2008-10-29 53 views
11

问题很简单。一个对象需要通知一些观察者可能感兴趣的事件。Ruby中.NET事件的等价物是什么?

当我坐在验证一个我现在用Ruby制作的设计来验证它时,我发现自己难以实现对象事件。在.Net中,这将是一个单行的.Net也处理器方法签名验证等。例如

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

是否有一个EventDispatcher模块或我缺少的东西,我可以绑定到一个Ruby类?希望得到一个与Ruby最不让人惊讶的原则一致的答案。 一个事件将是事件的名称加上[observer,methodName]对象的队列,当事件发生时需要调用该对象。

回答

2
+1

不完全......这似乎是观察者设计模式的实现。我需要的是锅炉对象公开多个事件,例如WaterExhausted,Started等,以及客户订阅他们的处理程序(不限于一个'更新'方法) – Gishu 2008-10-29 09:19:04

8

首先,在Ruby中没有方法签名。最接近的就是检查函数的参数。鸭子打字需要不同的思考(略)。

Observable模块是一个开始,但如果您有要求从一个类获取多个事件,那么它可能不够。

这是一个快速草图。它支持方法和块。根据需要进行修改以适应您的代码,线程方法等。例如,您可以使用method_missing在方法名称中使用事件名称,而不是将其作为参数。

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

我回音,有没有在Ruby语言级模拟到.NET的事件。轨道处理的方式是ActiveSupport::Callbacks(该页面上有一个示例)。

+0

推测是因为Ruby是一种语言,而不是框架? – 2008-11-03 13:48:54

6

为什么不写你自己的事件类?类似于

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 

这只是一个最小样本,但可以用任何方式进行扩展。在.Net中添加sender或eventArgs等参数,或者任何你喜欢的东西;-)

0

我为此写了一个gem,因为我有完全相同的问题。试试这个:

gem install ruby_events 

按照说明上http://github.com/nathankleyn/ruby_events,但简而言之:

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

快速注意这一点。我建议你看看EventMachine

这是不同的外观同样的想法。它实现了一个事件驱动的范例,因此您比.Net Events的等同物高一级,并将EventMachine模块视为CLR事件处理程序。同样退后一步,Ruby遵循Smalltalk处理模型,其中对方法的任何调用都是发送到对象的消息(如同事件)(请参阅Send()方法)。 EventMachine给你的是事件上的单线程片。你可以使用Rack之类的东西来处理线程或工作者。

0

我是一个noob,但Ruby似乎真的很强大。您可以自己实现C#风格的事件是这样的:

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

我创建了一个宝石究竟做你想要什么,令人惊讶的叫event_dispatcher如你所提到的。我希望它可以帮助别人:event_dispatcher

相关问题