Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the equivalent of .NET events in Ruby?

Tags:

oop

ruby

events

The problem is very simple. An object needs to notify some events that might be of interest to observers.

When I sat to validate a design that I cooked up now in Ruby just to validate it.. I find myself stumped as to how to implement the object events. In .Net this would be a one-liner.. .Net also does handler method signature verification,etc. e.g.

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

Is there an EventDispatcher module or something that I am missing that I can strap on to a Ruby class ? Hoping for an answer that plays along with Ruby's principle of least surprise. An event would be the name of the event plus a queue of [observer, methodName] objects that need to be invoked when the event takes place.

like image 269
Gishu Avatar asked Oct 29 '08 08:10

Gishu


2 Answers

Firstly, in Ruby there are no method signatures. The closest would be checking the function's arity. Duck typing requires thinking differently (slightly).

The Observable module is a start, but if you have a requirement to have multiple events from a single class it might not be enough.

This is a quick sketch. It supports methods and blocks. Modify as necessary to adapt for your code, threading approach, etc. For example, you could use method_missing to have the event name in the method name rather than having it as a parameter.

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 [email protected]_key?(name)
        @listeners[name].delete_if { |o| o == func }
    end

    def trigger_event(name, *args)
        return if [email protected]_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")
like image 89
janm Avatar answered Oct 12 '22 23:10

janm


Why not write your own event class? Something like

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

This is just a minimal sample, but can be extended in any ways. Add parameters like sender and eventArgs in .Net, or whatever you like ;-)

like image 27
D. Rexin Avatar answered Oct 12 '22 22:10

D. Rexin