Что эквивалентно событиям .NET в Ruby? - PullRequest
11 голосов
/ 29 октября 2008

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

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

Ответы [ 9 ]

8 голосов
/ 29 октября 2008

Во-первых, в 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 !@listeners.has_key?(name)
        @listeners[name].delete_if { |o| o == func }
    end

    def trigger_event(name, *args)
        return if !@listeners.has_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")
6 голосов
/ 05 февраля 2010

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

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

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

2 голосов
/ 03 ноября 2008

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

2 голосов
/ 29 октября 2008

Наблюдаемый модуль ?

1 голос
/ 10 апреля 2012

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

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

0 голосов
/ 28 декабря 2013

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

0 голосов
/ 16 июля 2012

Я новичок, но Руби кажется действительно сильным. Вы можете реализовать события в стиле 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 голосов
/ 07 марта 2011

Краткое примечание по этому вопросу. Я предлагаю вам посмотреть на EventMachine

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

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

0 голосов
/ 29 апреля 2010

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

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!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...