Sunday, April 3, 2011

How to do Events in 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?

From stackoverflow
  • 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
    
  • 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.

    Ash : I want to use the Observer Design Pattern.
  • 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.

0 comments:

Post a Comment