ruby 40 lines · 7 steps

Building an EventEmitter in Ruby

A pub/sub class that registers blocks per event and fires them on emit, including one-shot listeners.

Explained by highlit
1class EventEmitter
2 def initialize
3 @listeners = Hash.new { |hash, key| hash[key] = [] }
4 end
5 
6 def on(event, &block)
7 raise ArgumentError, "a block is required" unless block_given?
8 
9 @listeners[event] << block
10 block
11 end
12 
13 def once(event, &block)
14 wrapper = nil
15 wrapper = lambda do |*args|
16 off(event, wrapper)
17 block.call(*args)
18 end
19 @listeners[event] << wrapper
20 wrapper
21 end
22 
23 def off(event, handler)
24 @listeners[event].delete(handler)
25 @listeners.delete(event) if @listeners[event].empty?
26 end
27 
28 def emit(event, *args)
29 return false unless @listeners.key?(event)
30 
31 @listeners[event].dup.each do |listener|
32 listener.call(*args)
33 end
34 true
35 end
36 
37 def listeners(event)
38 @listeners[event].dup
39 end
40end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A Hash with a default block lets you append to per-key collections without checking existence first.
  2. 2Capturing a lambda in a variable it references lets the callback unregister itself when it runs.
  3. 3Duplicating a collection before iterating protects you from listeners that mutate it mid-loop.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building an EventEmitter in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code