Using More Rubyesq Events In Ruby
In my previous post I showed a way to use events in Ruby in a way that is very similar to how it is in C#. Somebody left a comment saying that I should take off my C# hat, and do it in a more typical Ruby way. I agree with that so I wanted to create an implementation based on his comment. I also wanted to avoid opening up the Object class and requiring you to mix-in a module in order to use the events.
The goal is basically to declare an event like this:
And subscribing to the event would be done like this:
and if you subscribed with a method, you should be able to unsubscribe like this:
This was again pretty simple to implement, though this implementation is not a robust as it could be (so keep that in mind if you ever decide to use this approach). First of all, we again start off with the Event class, which now looks like this:
Nothing special here, except that the add method can accept a Method instance, a block, or both. The default value of the method parameter is nil so you can skip it if you only want to hook a block to the event.
And then we have the EventPublisher module that you can mix-in (more on this in a bit) to your class:
You might be wondering what the following line does:
event = send(symbol)
This dynamically calls the method with the given symbol. In our case, that would be the getter method to access the event, which we only create during the registration of an event, so we can't call this method like we'd normally do.
Also note that there is no way to unsubscribe a block from the event. Well, there might be a way but I simply don't know how to do it, since a block is not an object and it has no identity. AFAIK (and again, I'm a ruby n00b) there is no good way to compare blocks, so we can't unsubscribe them from events either. So you really only want to subscribe blocks to an event if you're sure that the event will not be published at a time when you don't want the block to be executed. Also, keep in mind that any variables that the block closes on will be kept in memory, so if your block closes on object references, their instances will be kept in memory until the publisher of the event is garbage collected.
If you need to be sure that you can remove the behavior you've added to an event, subscribe with a Method instance and unsubscribe when you need the added behavior removed again!
Now we can define our Publisher from the previous post like this:
The EventPublisher module is used as a mixin in the Publisher class. Simply put, that means that the methods defined in EventPublisher are now a part of the Publisher class as well (including their definition), and the nice thing about it is that we didn't have to inherit from a base class to inherit this extra functionality.
We also have the following two classes:
The first class subscribes to the event through a Method instance, the second simply assigns a block. As you can see, the first class can unsubscribe from the event by passing the Method instance to the unsubscribe method for that given event.
And if we run the following code:
We get the following output:
SubscriberWithMethod received: hello world! block received: hello world! block received: hello world!
Written by Davy Brion, published on 2010-08-23 23:12:35
comments powered by Disqus