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

Ответ 1

Во-первых, в Ruby нет сигнатур методов. Ближайшим будет проверка функции arity. Утиная печать требует мышления по-другому (слегка).

Модуль 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

Почему бы не написать собственный класс событий? Что-то вроде

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 или что вам угодно; -)

Ответ 4

Я бы сказал, что в Ruby для .NET-событий нет аналогового уровня. То, как это связано с рельсами, - это ActiveSupport:: Callbacks (на этой странице есть пример).

Ответ 5

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

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

Ответ 6

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

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!

Ответ 7

Быстрая заметка об этом. Я предлагаю вам посмотреть EventMachine

Это другой взгляд той же идеи. Он реализует парадигму, управляемую событиями, поэтому вы на один уровень выше эквивалента для .Net-событий и рассматриваете модуль EventMachine как обработчик событий CLR.

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

Ответ 8

Я noob, но Ruby кажется действительно мощным. Вы можете сами реализовать события стиля С# следующим образом:

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

Ответ 9

Я создал драгоценный камень, который делает именно то, что вы хотите, и на удивление назвал event_dispatcher, как вы упомянули. Я надеюсь, что он поможет кому-то: event_dispatcher