Что эквивалентно событиям .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 или что вам угодно; -)
Ответ 3
Наблюдаемый модуль?
Ответ 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