Module: Events::Emitter

Included in:
EventEmitter
Defined in:
lib/events.rb

Overview

The Events::Emitter mixin provides a clone of the Node.js EventEmitter API for Ruby.

Instances of classes including Events::Emitter will emit events, events are represented by symbols, underscored in the usual Ruby fashion. Examples: :connection, :data, :message_begin

Blocks can be attached to objects to be executed when an event is emitted, these blocks are called listeners.

All EventEmitters emit the event :new_listener when new listeners are added, this listener is provided with the event and new listener added. Example:

server.on(:new_listener) do |event, listener|
  puts "added new listener #{listener} for event #{event}"
end

server.on(:connection) do |socket|
  puts "someone connected!"
end

Outputs “added new listener #<Proc:[email protected]:12> for event connection”.

When an EventEmitter experiences an error, the typical action is to emit an :error event. Error events are special – if there is no handler for them they raise an Events::UncaughtError exception.

Defined Under Namespace

Classes: Listeners, OnceWrapper

Constant Summary collapse

DEFAULT_MAX_LISTENERS =
10

Instance Method Summary collapse

Instance Method Details

#add_listener(event, proc = nil, &block) ⇒ Object Also known as: on

:call-seq: emitter.on(event) {|args…| block} -> emitter emitter.on(event, proc) -> emitter

Adds a listener to the end of the listeners array for the specified event.

server.on(:connection) do |socket|
  puts "someone connected!"
end

Rather than a block, can take a second argument of a Proc (or any object with a #call method).



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/events.rb', line 99

def add_listener(event, proc=nil, &block)
  listener = proc || block
  unless listener.respond_to?(:call)
    raise ArgumentError.new("Listener must respond to #call")
  end
  emit(:new_listener, event, listener)
  
  event_listeners = listeners(event)
  event_listeners.push(listener)
  
  current = event_listeners.length
  if max_listeners > 0 && current > max_listeners && !event_listeners.warned
    warn(caller[1] +
      ": warning: possible EventEmitter memory leak detected. " <<
      "#{current} listeners added. " <<
      "Use Emitter#max_listeners = n to increase limit.")
    event_listeners.warned = true
  end
  
  self
end

#emit(event, *args) ⇒ Object

:call-seq: emitter.emit(event[, arguments…]) -> bool

Execute each of the listeners in order with the supplied arguments.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/events.rb', line 72

def emit(event, *args)
  listeners = @listeners && @listeners.key?(event) && @listeners[event]
  
  if event == :error && (!listeners || listeners.empty?)
    raise *args if args.first.respond_to?(:exception)
    raise Events::UncaughtError, args.first if args.first.is_a?(String)
    raise Events::UncaughtError.new("Uncaught, unspecified 'error' event.")
  elsif listeners
    listeners.dup.each do |listener|
      listener.call(*args)
    end.any?
  else
    false
  end
end

#listeners(event) ⇒ Object

:call-seq: emitter.listeners(event) -> array

Returns an array of listeners for the specified event. This array can be manipulated, e.g. to remove listeners.



64
65
66
# File 'lib/events.rb', line 64

def listeners(event)
  (@listeners ||= Hash.new {|hash, key| hash[key] = Listeners.new})[event]
end

#max_listeners=(value) ⇒ Object Also known as: set_max_listeners

:call-seq: emitter.max_listeners = integer -> integer

By default an EventEmitter will print a warning if more than 10 listeners are added to it. This is a useful default which helps finding memory leaks. Obviously not all Emitters should be limited to 10. This method allows that to be increased. Set to zero for unlimited.



54
55
56
# File 'lib/events.rb', line 54

def max_listeners=(value)
  @max_listeners = value
end

#once(event, proc = nil, &block) ⇒ Object

:call-seq: emitter.once(event) {|args…| block} -> emitter emitter.once(event, proc) -> emitter

Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.

server.once(:connection) do |socket|
  puts "Ah, we have our first user!"
end


131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/events.rb', line 131

def once(event, proc=nil, &block)
  listener = proc || block
  unless listener.respond_to?(:call)
    raise ArgumentError.new("Listener must respond to #call")
  end
  once = OnceWrapper.new do |*args|
    remove_listener(event, once)
    listener.call(*args)
  end
  once.original = listener
  
  add_listener(event, once)
end

#remove_all_listeners(event = :_remove_all_listeners_default_arg_) ⇒ Object

:call-seq: emitter.remove_all_listeners -> emitter emitter.remove_all_listeners(event) -> emitter

Removes all listeners, or those of the specified event.



164
165
166
167
168
169
170
171
# File 'lib/events.rb', line 164

def remove_all_listeners(event=:_remove_all_listeners_default_arg_)
  @listeners = nil if event == :_remove_all_listeners_default_arg_
  if @listeners && @listeners.key?(event)
    @listeners[event].clear
    @listeners.delete(event)
  end
  self
end

#remove_listener(event, proc) ⇒ Object

:call-seq: emitter.remove_listener(event, proc) -> emitter

Remove a listener from the listener array for the specified event.



149
150
151
152
153
154
155
156
157
# File 'lib/events.rb', line 149

def remove_listener(event, proc)
  if @listeners && @listeners.key?(event)
    @listeners[event].delete_if do |lis|
      lis == proc || lis.respond_to?(:original) && lis.original == proc
    end
    @listeners.delete(event) if @listeners[event].empty?
  end
  self
end