Class: Observatory::Dispatcher
- Inherits:
-
Object
- Object
- Observatory::Dispatcher
- Defined in:
- lib/observatory/dispatcher.rb
Overview
The Dispatcher is the central repository of all registered observers, and is used by observables to send out signals to these observers.
A dispatcher is not a singleton object, which means you may very well have several dispatcher objects in your program, keeping track of different stacks of observers and observables. Note that this requires you to pass your dispatcher object around using dependency injection.
## For observables
The stack of observers for any given signal is kept in #observers. When using #notify, #notify_until or #filter all observers in the stack will be called.
### Notification methods
Observable objects may use the following methods to trigger their observers:
-
#notify to call all observers.
-
#notify_until to call observers until one stops the chain.
-
#filter to let all observers alter a given value.
## For observers
An object that observes another object is an observer, and it can register itself with the Dispatcher to listen to a signal that observable objects may issue.
An observer may be anything that is callable, but will usually be a method, block or Proc object. You may optionally specify an explicit priority for an observer, to make sure it gets called before or after other observers.
Instance Attribute Summary collapse
-
#observers ⇒ Hash
readonly
A list of all registered observers grouped by signal.
Instance Method Summary collapse
-
#connect(signal, *args, &block) ⇒ #call
Register a observer for a given signal.
-
#disconnect(signal, observer) ⇒ #call?
Removes an observer from a signal stack, so it no longer gets triggered.
-
#filter(event, value) ⇒ Event
Let all registered observers modify a given value.
-
#initialize ⇒ Dispatcher
constructor
A new instance of Dispatcher.
-
#notify(event) ⇒ Event
Send out a signal to all registered observers using a new Event instance.
-
#notify_until(event) ⇒ Event
Same as #notify, but halt execution as soon as an observer has indicated it has handled the event by returning a non-falsy value.
Constructor Details
#initialize ⇒ Dispatcher
Returns a new instance of Dispatcher.
76 77 78 |
# File 'lib/observatory/dispatcher.rb', line 76 def initialize @observers = {} end |
Instance Attribute Details
#observers ⇒ Hash (readonly)
A list of all registered observers grouped by signal.
74 75 76 |
# File 'lib/observatory/dispatcher.rb', line 74 def observers @observers end |
Instance Method Details
#connect(signal, observer, options = {}) ⇒ #call #connect(signal, options = {}, &block) ⇒ #call
Register a observer for a given signal.
Instead of adding a method or Proc object to the stack, you could also use a block. Either the observer argument or the block is required.
Optionally, you could pass in an options hash as the last argument, that can specify an explicit priority. When omitted, an internal counter starting from 1 will be used. To make sure your observer is called last, specify a high, positive number. To make sure your observer is called first, specify a high, negative number.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/observatory/dispatcher.rb', line 132 def connect(signal, *args, &block) # ugly argument parsing. # Make sure that there is either a block given, or that the second argument is # something callable. If there is a block given, the second argument, if given, # must be a Hash which defaults to an empty Hash. If there is no block given, # the third optional argument must be Hash. if block_given? observer = block if args.size == 1 && args.first.is_a?(Hash) = args.first elsif args.size == 0 = {} else raise ArgumentError, 'When given a block, #connect only expects a signal and options hash as arguments' end else observer = args.shift raise ArgumentError, 'Use a block, method or proc to specify an observer' unless observer.respond_to?(:call) if args.any? = args.shift raise ArgumentError, '#connect only expects a signal, method and options hash as arguments' unless .is_a?(Hash) || args.any? else = {} end end observer_with_priority = { :observer => observer, :priority => ([:priority] || next_internal_priority) } # Initialize the list of observers for this signal and add this observer observers[signal] ||= [] observers[signal] << observer_with_priority # Sort all observers on priority observers[signal].sort! do |a,b| a[:priority] <=> b[:priority] end observer end |
#disconnect(signal, observer) ⇒ #call?
Removes an observer from a signal stack, so it no longer gets triggered.
181 182 183 184 185 186 |
# File 'lib/observatory/dispatcher.rb', line 181 def disconnect(signal, observer) return nil unless observers.key?(signal) observers[signal].delete_if do |observer_with_priority| observer_with_priority[:observer] == observer end end |
#filter(event, value) ⇒ Event
Let all registered observers modify a given value. The observable can then use the Event#return_value to get the filtered result back.
You could use #filter to let observers modify arguments to a method before continuing to work on them (just an example).
228 229 230 231 232 233 234 |
# File 'lib/observatory/dispatcher.rb', line 228 def filter(event, value) each(event.signal) do |observer| value = observer.call(event, value) end event.return_value = value event end |
#notify(event) ⇒ Event
Send out a signal to all registered observers using a new Event instance. The Event#signal will be used to determine the stack of #observers to use.
Using #notify allows observers to take action at a given time during program execution, such as logging important events.
197 198 199 200 201 202 |
# File 'lib/observatory/dispatcher.rb', line 197 def notify(event) each(event.signal) do |observer| observer.call(event) end event end |
#notify_until(event) ⇒ Event
Same as #notify, but halt execution as soon as an observer has indicated it has handled the event by returning a non-falsy value.
An event that was acted upon by an observer will be marked as processed.
212 213 214 215 216 217 |
# File 'lib/observatory/dispatcher.rb', line 212 def notify_until(event) each(event.signal) do |observer| event.process! and break if observer.call(event) end event end |