Как сделать события в Ruby? - PullRequest
16 голосов
/ 03 марта 2009

Я из C # и только начал программировать на Ruby. Дело в том, что мне нужно знать, как я могу вызывать события в моих классах, чтобы разные наблюдатели могли запускаться, когда что-то должно происходить.

Проблема в том, что в моих книгах по Ruby даже не упоминаются события, не говоря уже о примерах. Кто-нибудь может мне помочь?

Ответы [ 5 ]

21 голосов
/ 03 марта 2009

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

4 голосов
/ 03 марта 2009

Я попытался написать библиотеку GUI на Ruby с небольшим C и, прежде всего, Ruby. Это оказалось настолько медленным, что я сдался и не выпустил его. Но я написал для него систему событий, которую я постарался сделать проще, чем в C #. Я переписал его пару раз, чтобы его было проще использовать. Я надеюсь, что это несколько полезно.

class EventHandlerArray < Array
  def add_handler(code=nil, &block)
    if(code)
      push(code)
    else
      push(block)
    end
  end
  def add
    raise "error"
  end
  def remove_handler(code)
    delete(code)
  end
  def fire(e)
    reverse_each { |handler| handler.call(e) }
  end
end

# with this, you can do:
#  event.add_handler
#  event.remove_handler
#  event.fire (usually never used)
#  fire_event
#  when_event
# You just need to call the events method and call super to initialize the events:
#  class MyControl
#    events :mouse_down, :mouse_up,
#           :mouse_enter, :mouse_leave
#    def initialize
#      super
#    end
#    def when_mouse_up(e)
#      # do something
#    end
#  end
#  control = MyControl.new
#  control.mouse_down.add_handler {
#    puts "Mouse down"
#  }
# As you can see, you can redefine when_event in a class to handle the event.
# The handlers are called first, and then the when_event method if a handler didn't
# set e.handled to true. If you need when_event to be called before the handlers,
# override fire_event and call when_event before event.fire. This is what painting
# does, for handlers should paint after the control.
#  class SubControl < MyControl
#    def when_mouse_down(e)
#      super
#      # do something
#    end
#  end
def events(*symbols)
  # NOTE: Module#method_added

  # create a module and 'include' it
  modName = name+"Events"
  initStr = Array.new
  readerStr = Array.new
  methodsStr = Array.new
  symbols.each { |sym|
    name = sym.to_s
    initStr << %Q{
      @#{name} = EventHandlerArray.new
    }
    readerStr << ":#{name}"
    methodsStr << %Q{
      def fire_#{name}(e)
        @#{name}.fire(e)
        when_#{name}(e) if(!e.handled?)
      end
      def when_#{name}(e)
      end
    }
  }
  eval %Q{
    module #{modName}
      def initialize(*args)
        begin
          super(*args)
        rescue NoMethodError; end
        #{initStr.join}
      end
      #{"attr_reader "+readerStr.join(', ')}
      #{methodsStr.join}
    end
    include #{modName}
  }
end

class Event
  attr_writer :handled
  def initialize(sender)
    @sender = @sender
    @handled = false
  end
  def handled?; @handled; end
end
3 голосов
/ 02 августа 2013

Очень простой слушатель Ruby. Это не совсем замена для событий .NET, но это очень простой пример очень простого слушателя.

module Listenable

    def listeners() @listeners ||= [] end

    def add_listener(listener)
        listeners << listener
    end

    def remove_listener(listener)
        listeners.delete listener
    end

    def notify_listeners(event_name, *args)
        listeners.each do |listener|
            if listener.respond_to? event_name
                listener.__send__ event_name, *args
            end
        end
    end

end

Для использования:

class CowListenable
    include Listenable

    def speak
        notify_listeners :spoken, 'moooo!'
    end

end

class CowListener

    def initialize(cow_listenable)
        cow_listenable.add_listener self
    end

    def spoken(message)
        puts "The cow said '#{message}'"
    end

end

cow_listenable = CowListenable.new
CowListener.new(cow_listenable)
cow_listenable.speak

Выход:

The cow said 'moooo!'
1 голос
/ 28 декабря 2013

Раскрытие информации: я хранитель event_aggregator драгоценного камня

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

Существует драгоценный камень, который помогает вам с этим, называется event_aggregator. С его помощью вы можете сделать следующее:

#!/usr/bin/ruby

require "rubygems"
require "event_aggregator"

class Foo
    include EventAggregator::Listener
    def initialize()
        message_type_register( "MessageType1", lambda{|data| puts data } )

        message_type_register( "MessageType2", method(:handle_message) )
    end

    def handle_message(data)
        puts data
    end

    def foo_unregister(*args)
        message_type_unregister(*args)
    end
end

class Bar
    def cause_event
        EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3]).publish
    end
    def cause_another_event
        EventAggregator::Message.new("MessageType2", ["Some More Stuff",2,3]).publish
    end
end

f = Foo.new

b = Bar.new
b.cause_event
b.cause_another_event
# => Some Stuff
     2
     3
# => Some More Stuff
     2
     3

Имейте в виду, что по умолчанию он асинхронный, поэтому, если вы выполните только этот сценарий, сценарий может завершиться до того, как будут переданы события. Чтобы отключить асинхронное поведение, используйте:

EventAggregator::Message.new("MessageType1", ["Some Stuff",2,3], false).publish
#The third parameter indicates async

Надеюсь, это может быть полезно в вашем случае

0 голосов
/ 03 марта 2009

Я не совсем уверен, что вы имеете в виду, но вы, вероятно, могли бы использовать исключения в ваших классах и вызывать их для определенных "событий". Если вам нужно событие для разработки GUI, тогда большинство GUI-сред определяют свой собственный стиль обработки событий.

Надеюсь, это несколько ответит на ваш вопрос.

...