如何在 Ruby 中进行事件?

发布于 2024-07-15 05:29:44 字数 129 浏览 6 评论 0原文

我有 C# 背景,刚刚开始使用 Ruby 编程。 问题是,我需要知道如何在类中引发事件,以便在需要发生事情时可以触发各种观察者。

问题是我关于 Ruby 的书籍甚至没有提到事件,更不用说提供示例了。 有人能帮助我吗?

I come from a C# background, and have just started programming in Ruby. The thing is, that I need to know how I can raise events in my classes so that various observers can be triggered when things need to happen.

The problem is the books I have on Ruby don't even mention events, let alone provide examples. Is anyone able to help me?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

妄断弥空 2024-07-22 05:29:44

问题已经得到解答,但是有一个观察者如果您想看看的话,它就内置在标准库中。 我过去曾在一个小型游戏项目中使用过它,效果非常好。

The question has already been answered, but there's an observer built right into the standard library if you want to give that a look. I've used it in the past for a small game project, and it works very well.

苍白女子 2024-07-22 05:29:44

极其简单的 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!'

Extremely simple Ruby listener. This is not exactly a replacement for .NET events, but this one is an extremely simple example of a very simple listener.

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

To use:

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

Output:

The cow said 'moooo!'
北音执念 2024-07-22 05:29:44

我尝试用 Ruby 编写一个 GUI 库,并使用一点 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

I tried writing a GUI library in Ruby with a little C and primarily Ruby. It ended up being so slow I gave up and never released it. But I wrote an event system for it that I tried to make easier than C#'s. I rewrote it a couple times to make it easier to use. I hope it is somewhat helpful.

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
坏尐絯℡ 2024-07-22 05:29:44

披露:我是 event_aggregator gem 的维护者,

具体取决于您想要如何解决问题,您可能会使用事件聚合器。 通过这种方式,您可以发布某种类型的消息,然后让您的对象侦听您希望它们接收的类型。 在某些情况下,这可能比正常事件更好,因为对象之间的耦合非常松散。 事件生成者和侦听器不需要共享彼此的引用。

有一个名为 event_aggregator 的 gem 可以帮助您完成此任务。 使用它,您可以执行以下操作:

#!/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

希望这对您的情况有所帮助

Disclosure: I am the maintainer of the event_aggregator gem

Depending on how you want to approach the problem you could potentially use an event aggregator. This way you can publish messages of a certain type and then have your objects listen to the types you want them to receive. This can in certain cases be better than normal events because you get a very loose coupling between your objects. The event producer and listener does not need to share a reference to the other.

There is a gem that helps you with this called event_aggregator. With it you can do the following:

#!/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

Be aware that it is async by default, so if you execute just this script the script might exit before the events are passed. To disable async behaviour use:

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

Hopefully this can be helpful in your case

妄断弥空 2024-07-22 05:29:44

我不确定你的意思到底是什么,但你可能可以在你的类中使用异常并在某些“事件”上引发它们。 如果您需要事件进行 GUI 开发,那么大多数 GUI 框架都会定义自己的事件处理样式。

希望这能在一定程度上回答您的问题。

I'm not sure of exactly what you mean but you could probably use exceptions in your classes and raise them on certain "events". If you need event for GUI development then most GUI frameworks define their own event handling style.

Hope this somewhat answers you're question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文