views:

758

answers:

3

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?

+2  A: 

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
Jordan Miner
A: 

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.

nkassis
I want to use the Observer Design Pattern.
Ash
+5  A: 

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.

bojo