├── .yardopts ├── .rspec ├── Gemfile ├── spec ├── spec_helper.rb └── lib │ └── event_bus_spec.rb ├── Rakefile ├── .travis.yml ├── .gitignore ├── event_bus.gemspec ├── lib ├── event_bus │ └── registrations.rb └── event_bus.rb └── README.md /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | 4 | require 'event_bus' 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | RSpec::Core::RakeTask.new('spec') 4 | 5 | task :default => :spec 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | bundler_args: '' 3 | rvm: 4 | - 1.9.2 5 | - 1.9.3 6 | - 2.0.0 7 | - 2.1.0 8 | - jruby-19mode 9 | - rbx-2.1 10 | - rbx-2.2 11 | matrix: 12 | allow_failures: 13 | - rvm: rbx-2.1 14 | - rvm: rbx-2.2 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | Gemfile.lock 7 | InstalledFiles 8 | lib/bundler/man 9 | pkg 10 | rdoc 11 | .rvmrc 12 | spec/reports 13 | tags 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | 18 | # YARD artifacts 19 | .yardoc 20 | _yardoc 21 | doc/ 22 | -------------------------------------------------------------------------------- /event_bus.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'event_bus' 3 | s.version = '1.1.1' 4 | s.date = '2014-12-22' 5 | s.summary = 'A simple pubsub event bus for Ruby applications' 6 | s.description = 'event_bus provides support for application-wide events, without coupling the publishing and subscribing objects or classes to each other' 7 | s.authors = ['Kevin Rutherford'] 8 | s.email = 'kevin@rutherford-software.com' 9 | s.homepage = 'http://github.com/kevinrutherford/event_bus' 10 | 11 | s.add_development_dependency 'rake', '~> 10' 12 | s.add_development_dependency 'rspec', '~> 3' 13 | 14 | s.files = `git ls-files -- lib spec [A-Z]* .rspec .yardopts`.split("\n") 15 | s.test_files = `git ls-files -- spec`.split("\n") 16 | s.require_path = 'lib' 17 | 18 | end 19 | 20 | -------------------------------------------------------------------------------- /lib/event_bus/registrations.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | class EventBus 4 | 5 | private 6 | 7 | class Registrations 8 | include Singleton 9 | 10 | def announce(event_name, payload) 11 | full_payload = {event_name: event_name}.merge(payload) 12 | listeners.each do |listener| 13 | pass_event_to listener, event_name, full_payload 14 | end 15 | end 16 | 17 | def clear 18 | listeners.clear 19 | end 20 | 21 | def add_method(pattern, listener, method_name) 22 | listeners << Registration.new(pattern, listener, method_name) 23 | end 24 | 25 | def add_block(pattern, &blk) 26 | listeners << BlockRegistration.new(pattern, blk) 27 | end 28 | 29 | def on_error(&blk) 30 | @error_handler = blk 31 | end 32 | 33 | def remove_subscriber(subscriber) 34 | listeners.delete subscriber 35 | end 36 | 37 | def last_subscriber 38 | listeners.last 39 | end 40 | 41 | private 42 | def listeners 43 | @listeners ||= [] 44 | end 45 | 46 | def error_handler 47 | @error_handler 48 | end 49 | 50 | def pass_event_to(listener, event_name, payload) 51 | begin 52 | listener.respond(event_name, payload) 53 | rescue => error 54 | error_handler.call(listener.receiver, payload.merge(error: error)) if error_handler 55 | end 56 | end 57 | 58 | Registration = Struct.new(:pattern, :listener, :method_name) do 59 | def respond(event_name, payload) 60 | listener.send(method_name, payload) if pattern === event_name 61 | end 62 | 63 | def receiver 64 | listener 65 | end 66 | end 67 | 68 | BlockRegistration = Struct.new(:pattern, :block) do 69 | def respond(event_name, payload) 70 | block.call(payload) if pattern === event_name 71 | end 72 | 73 | def receiver 74 | block 75 | end 76 | end 77 | 78 | end 79 | 80 | end 81 | 82 | -------------------------------------------------------------------------------- /lib/event_bus.rb: -------------------------------------------------------------------------------- 1 | require_relative 'event_bus/registrations' 2 | 3 | class EventBus 4 | 5 | class << self 6 | 7 | # 8 | # Announce an event to any waiting listeners. 9 | # 10 | # The +event_name+ is added to the +payload+ hash (with the key +:event_name+) 11 | # before being passed on to listeners. 12 | # 13 | # @param event_name [String, Symbol] the name of your event 14 | # @param payload [Hash] the information you want to pass to the listeners 15 | # @return [EventBus] the EventBus, ready to be called again. 16 | # 17 | def publish(event_name, payload = {}) 18 | case event_name 19 | when Symbol, String 20 | registrations.announce(event_name, payload) 21 | self 22 | else 23 | raise ArgumentError.new('The event name must be a string or a symbol') 24 | end 25 | end 26 | 27 | alias :announce :publish 28 | alias :broadcast :publish 29 | 30 | # 31 | # Subscribe to a set of events. 32 | # 33 | # If +blk+ is supplied, it will be called with any event whose name 34 | # matches +pattern+. 35 | # 36 | # If no block is given, and if +pattern+ is a String or a Regexp, 37 | # a method will be called on +listener+ whenever an event matching 38 | # +pattern+ occurs. In this case, if +method_name+ is supplied the 39 | # EventBus will look for, and call, a method of that name on +listener+; 40 | # otherwise if +method_name+ is not given, the EventBus will attempt to 41 | # call a method whose name matches the event's name. 42 | # 43 | # Finally, if no block is given and +pattern+ is not a String or a Regexp, 44 | # then +pattern+ is taken to be a listener object and the EventBus will 45 | # attempt to call a method on it whose name matches the event's name. 46 | # 47 | # Either +listener+ or +blk+ must be provided, both never both. 48 | # 49 | # When a matching event occurs, either the block is called or the +method_name+ 50 | # method on the +listener+ object is called. 51 | # 52 | # @param pattern [String, Regexp] listen for any events whose name matches this pattern 53 | # @param listener the object to be notified when a matching event occurs 54 | # @param method_name [Symbol] the method to be called on +listener+ when a matching event occurs 55 | # @return [EventBus] the EventBus, ready to be called again. 56 | # 57 | def subscribe(pattern, listener = nil, method_name = nil, &blk) 58 | case pattern 59 | when Regexp, String, Symbol 60 | subscribe_pattern(pattern, listener, method_name, &blk) 61 | else 62 | raise ArgumentError.new('You cannot give two listeners') if listener || method_name 63 | raise ArgumentError.new('You cannot give both a listener and a block') if block_given? 64 | subscribe_obj(pattern) 65 | end 66 | self 67 | end 68 | 69 | alias :listen_for :subscribe 70 | 71 | # 72 | # Register a global error handler 73 | # 74 | # The supplied block will be called once for each error that is raised by 75 | # any listener, for any event. 76 | # 77 | # The block will be provided with two parameters, the listener that errored, 78 | # and the payload of the event. 79 | # 80 | # @param blk the block to be called when any unhandled error occurs in a listener 81 | # @return [EventBus] the EventBus, ready to be called again 82 | def on_error(&blk) 83 | registrations.on_error &blk 84 | self 85 | end 86 | 87 | # 88 | # Delete all current listener registrations 89 | # 90 | # @return the EventBus, ready to be called again. 91 | # 92 | def clear 93 | registrations.clear 94 | self 95 | end 96 | 97 | # 98 | # Adds a subscriber that only listens for the duration of the block. Mostly 99 | # useful for testing event dispatching. 100 | # 101 | # @param pattern [String, Regexp] listen for any events whose name matches this pattern 102 | # @param listener the object to be notified when a matching event occurs 103 | # @param method_name [Symbol] the method to be called on +listener+ when a matching event occurs 104 | # @return [EventBus] the EventBus, ready to be called again. 105 | def with_temporary_subscriber(pattern, listener = nil, method_name = nil) 106 | subscribe(pattern, listener, method_name) 107 | temporary_subscriber = registrations.last_subscriber 108 | 109 | yield 110 | 111 | self 112 | ensure 113 | registrations.remove_subscriber(temporary_subscriber) 114 | end 115 | 116 | private 117 | 118 | def subscribe_pattern(pattern, listener, method_name, &blk) 119 | if listener 120 | raise ArgumentError.new('You cannot give both a listener and a block') if block_given? 121 | raise ArgumentError.new('You must supply a method name') unless method_name 122 | registrations.add_method(pattern, listener, method_name) 123 | else 124 | raise ArgumentError.new('You must provide a listener or a block') unless block_given? 125 | registrations.add_block(pattern, &blk) 126 | end 127 | end 128 | 129 | def subscribe_obj(listener) 130 | registrations.add_block(/.*/) do |payload| 131 | method = payload[:event_name].to_sym 132 | listener.send(method, payload) if listener.respond_to?(method) 133 | end 134 | end 135 | 136 | def registrations 137 | Registrations.instance 138 | end 139 | 140 | end 141 | 142 | end 143 | 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EventBus 2 | 3 | A simple pubsub event bus for Ruby applications. 4 | 5 | [![Build Status](https://travis-ci.org/kevinrutherford/event_bus.png)](https://travis-ci.org/kevinrutherford/event_bus) 6 | [![Dependency 7 | Status](https://gemnasium.com/kevinrutherford/event_bus.png)](https://gemnasium.com/kevinrutherford/event_bus) 8 | [![Code 9 | Climate](https://codeclimate.com/github/kevinrutherford/event_bus.png)](https://codeclimate.com/github/kevinrutherford/event_bus) 10 | 11 | * Gem: 12 | * API docs: 13 | * Source code: 14 | 15 | ## Features 16 | 17 | * Simple, global support for the Observer pattern, aka Publisher-Subscriber. 18 | * Publish and subscribe to events throughout your Ruby application. 19 | * Listen for events without coupling to the publishing object or class. 20 | * Subscribe to events using names or regex patterns. 21 | * Works with Rails. 22 | * Works without Rails. 23 | * Completely synchronous (use the 24 | [event_bg_bus](https://rubygems.org/gems/event_bg_bus) gem for async operation 25 | using Sidekiq). 26 | 27 | ## Installation 28 | 29 | Install the gem 30 | 31 | ``` 32 | gem install event_bus 33 | ``` 34 | 35 | Or add it to your Gemfile and run `bundle`. 36 | 37 | ``` ruby 38 | gem 'event_bus' 39 | ``` 40 | 41 | ## Usage 42 | 43 | ### Publishing events 44 | 45 | Publish events whenever something significant happens in your application: 46 | 47 | ```ruby 48 | class PlaceOrder 49 | //... 50 | EventBus.announce(:order_placed, order: current_order, customer: current_user) 51 | end 52 | ``` 53 | 54 | The event name (first argument) can be a String or a Symbol. 55 | The Hash is optional and supplies a payload of information to any subscribers. 56 | 57 | (If you don't like the method name `announce` you can use `publish` or 58 | `broadcast` instead.) 59 | 60 | ### Subscribing to events 61 | 62 | There are three ways to subscribe to events. 63 | 64 | 1. Subscribe a listener object: 65 | 66 | ```ruby 67 | EventBus.subscribe(StatsRecorder.new) 68 | ``` 69 | 70 | The event will be handled by a method whose name matches the event name: 71 | 72 | ```ruby 73 | class StatsRecorder 74 | def order_placed(payload) 75 | order = payload[:order] 76 | //... 77 | end 78 | end 79 | ``` 80 | 81 | If the object has no matching method, it doesn't receive the event. 82 | 83 | 2. Specify the method to be called when the event fires: 84 | 85 | ```ruby 86 | EventBus.subscribe(:order_placed, StatsRecorder.new, :print_order) 87 | ``` 88 | 89 | In this case the event will be handled by the `print_order` method: 90 | 91 | ```ruby 92 | class StatsRecorder 93 | def print_order(payload) 94 | order = payload[:order] 95 | //... 96 | end 97 | end 98 | ``` 99 | 100 | The first argument to `subscribe` can be a String, 101 | a Symbol or a Regexp: 102 | 103 | ```ruby 104 | EventBus.subscribe(/order/, StatsRecorder.new, :print_order) 105 | ``` 106 | 107 | 3. Subscribe a block: 108 | 109 | ```ruby 110 | EventBus.subscribe(:order_placed) do |payload| 111 | order = payload[:order] 112 | //... 113 | end 114 | ``` 115 | 116 | The argument to `subscribe` can be a String, a Symbol or a Regexp: 117 | 118 | ```ruby 119 | EventBus.subscribe(/order/) do |payload| 120 | order = payload[:order] 121 | //... 122 | end 123 | ``` 124 | 125 | See the specs for more detailed usage scenarios. 126 | 127 | ### Error Handling 128 | 129 | Any exception raised by the event listeners will be swallowed, and will not affect the execution flow. In order to handle errors you can register a global error handler: 130 | 131 | ```ruby 132 | EventBus.on_error do |listener, payload| 133 | #handle the error 134 | end 135 | ``` 136 | 137 | The supplied block will be called once for each error that is raised by any listener, for any event. 138 | 139 | The block will be provided with two parameters, the listener that errored, and the payload of the event. The payload of the event will have an extra `error` value containing the exception object that was raised: 140 | 141 | ```ruby 142 | EventBus.on_error do |listener, payload| 143 | puts listener.inspect # => # 144 | puts payload.inspect # => {:event_name=>:order_placed, :order_id=>1, :error=>#} 145 | puts payload[:error].inspect # => # 146 | end 147 | 148 | EventBus.subscribe(:order_placed) do |payload| 149 | raise StandardError, 'something went wrong' 150 | end 151 | 152 | EventBus.announce(:order_placed, order_id: 1) 153 | ``` 154 | 155 | ## Compatibility 156 | 157 | Tested with Ruby 2.1, 2.0, 1.9.x, JRuby. 158 | See the [build status](https://travis-ci.org/kevinrutherford/event_bus) 159 | for details. 160 | 161 | ## License 162 | 163 | (The MIT License) 164 | 165 | Copyright (c) 2013 Kevin Rutherford 166 | 167 | Permission is hereby granted, free of charge, to any person obtaining a copy of 168 | this software and associated documentation files (the 'Software'), to deal in 169 | the Software without restriction, including without limitation the rights to 170 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 171 | of the Software, and to permit persons to whom the Software is furnished to do 172 | so, subject to the following conditions: 173 | 174 | The above copyright notice and this permission notice shall be included in all 175 | copies or substantial portions of the Software. 176 | 177 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 178 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 179 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 180 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 181 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 182 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 183 | SOFTWARE. 184 | 185 | -------------------------------------------------------------------------------- /spec/lib/event_bus_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventBus do 4 | let(:listener) { double(:listener, handler: true) } 5 | 6 | before do 7 | EventBus.clear 8 | end 9 | 10 | describe 'a temporary subscriber' do 11 | example 'receives events during the subscription block' do 12 | expect(listener).to receive(:handler).with(event_name: 'test') 13 | EventBus.with_temporary_subscriber(/test/, listener, :handler) { EventBus.publish 'test'} 14 | end 15 | 16 | example 'does not receive events after the subscription block' do 17 | expect(listener).to_not receive(:handler).with(event_name: 'test') 18 | EventBus.with_temporary_subscriber(/test/, listener, :handler) { } 19 | EventBus.publish 'test' 20 | end 21 | end 22 | 23 | describe 'publishing' do 24 | 25 | it 'accepts a string for the event name' do 26 | expect(listener).to receive(:handler).with(event_name: 'aa123bb') 27 | EventBus.subscribe(/aa123bb/, listener, :handler) 28 | EventBus.publish('aa123bb') 29 | end 30 | 31 | it 'accepts a symbol for the event name' do 32 | expect(listener).to receive(:handler).with(event_name: :aa123bb) 33 | EventBus.subscribe(/aa123bb/, listener, :handler) 34 | EventBus.publish(:aa123bb) 35 | end 36 | 37 | it 'rejects any other type as the event name' do 38 | expect { EventBus.publish(123) }.to raise_exception(ArgumentError) 39 | end 40 | 41 | it 'adds the event name to the payload' do 42 | expect(listener).to receive(:handler).with(event_name: 'aa123bb', a: 56) 43 | EventBus.subscribe('aa123bb', listener, :handler) 44 | EventBus.publish('aa123bb', a: 56) 45 | end 46 | 47 | it 'allows the payload to be omitted' do 48 | expect(listener).to receive(:handler).with(event_name: 'aa123bb') 49 | EventBus.subscribe('aa123bb', listener, :handler) 50 | EventBus.publish('aa123bb') 51 | end 52 | 53 | end 54 | 55 | describe 'publishing with errors' do 56 | let(:error) { RuntimeError.new } 57 | let(:erroring_listener) { double(:erroring_listener) } 58 | let(:error_handler) { double(:error_handler, handle_error: true) } 59 | 60 | before do 61 | allow(erroring_listener).to receive(:handler) { raise error } 62 | end 63 | 64 | it 'sends the event to the second listener when the first errors' do 65 | expect(listener).to receive(:handler).with(event_name: 'aa123bb') 66 | EventBus.subscribe('aa123bb', erroring_listener, :handler) 67 | EventBus.subscribe('aa123bb', listener, :handler) 68 | EventBus.publish('aa123bb') 69 | end 70 | 71 | context 'with an error handler' do 72 | before do 73 | EventBus.on_error do |listener, payload| 74 | error_handler.handle_error(listener, payload) 75 | end 76 | end 77 | 78 | it 'when the listener is an object' do 79 | expect(error_handler).to receive(:handle_error).with(erroring_listener, event_name: 'aa123bb', error: error) 80 | EventBus.subscribe('aa123bb', erroring_listener, :handler) 81 | EventBus.publish('aa123bb') 82 | end 83 | 84 | it 'when the listener is a block' do 85 | expect(error_handler).to receive(:handle_error).with(instance_of(Proc), event_name: 'aa123bb', error: error) 86 | EventBus.subscribe('aa123bb') {|info| raise error } 87 | EventBus.publish('aa123bb') 88 | end 89 | 90 | end 91 | 92 | end 93 | 94 | describe 'subscribing' do 95 | 96 | context 'with a regex pattern' do 97 | it 'sends the event to a matching listener' do 98 | expect(listener).to receive(:handler).with(a: 1, b: 2, event_name: 'aa123bb') 99 | EventBus.subscribe(/123b/, listener, :handler) 100 | EventBus.publish('aa123bb', a: 1, b: 2) 101 | end 102 | 103 | it 'does not send the event to non-matching listeners' do 104 | expect(listener).to_not receive(:handler) 105 | EventBus.subscribe(/123a/, listener, :handler) 106 | EventBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') 107 | end 108 | end 109 | 110 | context 'with a string pattern' do 111 | it 'sends the event to a matching listener' do 112 | expect(listener).to receive(:handler).with(a: 1, b: 2, event_name: 'aa123bb') 113 | EventBus.subscribe('aa123bb', listener, :handler) 114 | EventBus.publish('aa123bb', a: 1, b: 2) 115 | end 116 | 117 | it 'does not send the event to non-matching listeners' do 118 | expect(listener).to_not receive(:handler) 119 | EventBus.subscribe('blah', listener, :handler) 120 | EventBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') 121 | end 122 | end 123 | 124 | context 'with a symbol pattern' do 125 | it 'sends the event to a matching listener' do 126 | expect(listener).to receive(:handler).with(a: 1, b: 2, event_name: :aa123bb) 127 | EventBus.subscribe(:aa123bb, listener, :handler) 128 | EventBus.publish(:aa123bb, a: 1, b: 2) 129 | end 130 | 131 | it 'does not send the event to non-matching listeners' do 132 | expect(listener).to_not receive(:handler) 133 | EventBus.subscribe(:blah, listener, :handler) 134 | EventBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') 135 | end 136 | end 137 | 138 | context 'subscribing a block' do 139 | let(:spy) { double(:spy, block_called: nil) } 140 | 141 | before { 142 | EventBus.subscribe('aa123bb') {|info| spy.block_called(info) } 143 | } 144 | 145 | it 'calls the block when the event matches' do 146 | expect(spy).to receive(:block_called).with(a: 1, b: 2, event_name: 'aa123bb') 147 | EventBus.publish('aa123bb', a: 1, b: 2) 148 | end 149 | 150 | it 'does not call the block when the event does not match' do 151 | expect(spy).to_not receive(:block_called) 152 | EventBus.publish('blah') 153 | end 154 | end 155 | 156 | context 'with a listener object' do 157 | before { EventBus.subscribe(listener) } 158 | 159 | it 'calls a listener method whose name matches the event name' do 160 | expect(listener).to receive(:handler).with(a: 2, b: 3, event_name: 'handler') 161 | EventBus.publish('handler', a: 2, b: 3) 162 | end 163 | 164 | it 'calls a listener method with symbol whose name matches the event name' do 165 | expect(listener).to receive(:handler).with(a: 2, b: 3, event_name: :handler) 166 | EventBus.publish(:handler, a: 2, b: 3) 167 | end 168 | 169 | it 'calls no method when there is no name match' do 170 | expect(listener).to_not receive(:handler) 171 | EventBus.publish('b_method') 172 | end 173 | 174 | end 175 | 176 | context 'when specifying the event name' do 177 | 178 | example 'a method or a block must be provided' do 179 | expect { EventBus.subscribe('blah', listener) }.to raise_exception(ArgumentError) 180 | end 181 | 182 | example 'a method AND a block cannot both be given' do 183 | expect { EventBus.subscribe('blah', listener, :handler) {|info| }}.to raise_exception(ArgumentError) 184 | end 185 | 186 | example 'a block must be provided when no method is supplied' do 187 | expect { EventBus.subscribe('blah') }.to raise_exception(ArgumentError) 188 | end 189 | 190 | end 191 | 192 | context 'when specifying a listener object' do 193 | 194 | example 'a method must not be given' do 195 | expect { EventBus.subscribe(listener, double) }.to raise_exception(ArgumentError) 196 | end 197 | 198 | example 'a block must not be given' do 199 | expect { EventBus.subscribe(listener) {|info| } }.to raise_exception(ArgumentError) 200 | end 201 | 202 | end 203 | 204 | end 205 | 206 | describe '.clear' do 207 | it 'removes all previous registrants' do 208 | EventBus.subscribe('aa123bb', listener, :handler) 209 | EventBus.clear 210 | expect(listener).to_not receive(:handler) 211 | EventBus.publish('aa123bb', {}) 212 | end 213 | 214 | end 215 | 216 | context 'when calling several EventBus methods' do 217 | 218 | example 'clear() can be cascaded' do 219 | expect(EventBus.clear).to be == EventBus 220 | end 221 | 222 | example 'publish() can be cascaded' do 223 | expect(EventBus.publish('aa123bb', {})).to be == EventBus 224 | end 225 | 226 | example 'subscribe() can be cascaded' do 227 | expect(EventBus.subscribe('aa123bb', listener, :handler)).to be == EventBus 228 | end 229 | 230 | example 'with_temporary_subscriber() can be cascaded' do 231 | expect(EventBus.with_temporary_subscriber('aa123bb', listener, :handler) { }).to be == EventBus 232 | end 233 | end 234 | 235 | end 236 | 237 | --------------------------------------------------------------------------------