2008-10-29 3 views
11

Проблема очень проста. Объект должен уведомлять о некоторых событиях, которые могут представлять интерес для наблюдателей.Что такое эквивалент событий .NET в Ruby?

Когда я сидел, чтобы проверить дизайн, который я приготовил сейчас в 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? Надеюсь на ответ, который играет с принципом наименьшего удивления Руби. Событие будет именем события плюс очередь объектов [observer, methodName], которые необходимо вызвать при возникновении события.

ответ

2
+1

Не совсем. Это похоже на реализацию шаблона проектирования Observer. Мне нужно, чтобы объект котла выставил несколько событий, таких как 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 

Это всего лишь минимальный образец, но может быть расширен любым способом. Добавьте параметры, такие как отправитель и eventArgs в .Net, или что вам нравится ;-)

0

Я написал драгоценный камень только для этого, потому что у меня была такая же проблема. Попробуйте это:

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-событий и рассматриваете модуль EventMachine как обработчик событий CLR.

Также, сделав шаг назад, Ruby следует модели обработки Smalltalk, где любой вызов метода представляет собой сообщение (как и событие), отправленное объекту (см. Метод Send()). EventMachine дает вам однопоточный фрагмент событий.Вы можете использовать что-то вроде Rack для обработки потоков или рабочих.

1

Взгляните на различные ruby state machine libraries. Они намереваются решить большую проблему, чем просто события, но могут предоставить вам решение.

Я успешно использовал жемчужину state_machine, , который включает events.

0

Я ноб, но 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