event_bg_bus 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZWIwZTQxNTY3YjBiYWYxMWUyOGE3MWJmMmZhZTAzZGMyNTc5NmYwYw==
5
+ data.tar.gz: !binary |-
6
+ M2NjYzhiOTIwMzE2MDIxN2M1ZTRjZTYyM2I0YmYyZWMxMDllN2E2ZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NmVmZDBhZjE3NTE2Y2JjMTJhOGM4YWMzMTQ3NzVhNmE0ZDJlMzcwYzQ1OWU2
10
+ YmQ1YWY0MTNkMzk5MjE4N2QxY2U1YTk4YzM0NWYzM2MyNWFjMTA4ZjdjOTVl
11
+ Y2IxMzBmMDlkMWU0MWQ3N2Q1NDEyMGI2NzE5YThkMTBhMTVlODk=
12
+ data.tar.gz: !binary |-
13
+ ZTUxYzRiYWFhMjJiYzlmMWQ3NjVmMjZhODFiNWI3YjljNTNkYTUzODAxMDg0
14
+ MjdiZWI3OTY1YWQyY2ViZmE4OWZlMWQ3OWI0NTE0ODE0NmJjNTFjMzAxZDY2
15
+ YTlmODgxYjg0NTZmZGQzNzkwMmY0ZjRmOThmOTllZjA1ZmJhYTc=
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'sidekiq'
5
+
6
+ group :test do
7
+ gem 'rspec', '~> 2.12'
8
+ gem 'rspec-spies'
9
+ gem 'rspec-given'
10
+ gem 'rake', '~> 10.0.1'
11
+ gem 'simplecov', :require => false
12
+ gem 'json'
13
+ end
14
+
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ event_bg_bus (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ celluloid (0.15.2)
10
+ timers (~> 1.1.0)
11
+ connection_pool (2.0.0)
12
+ diff-lcs (1.2.4)
13
+ json (1.8.0)
14
+ multi_json (1.7.3)
15
+ rake (10.0.4)
16
+ redis (3.0.7)
17
+ redis-namespace (1.4.1)
18
+ redis (~> 3.0.4)
19
+ rspec (2.13.0)
20
+ rspec-core (~> 2.13.0)
21
+ rspec-expectations (~> 2.13.0)
22
+ rspec-mocks (~> 2.13.0)
23
+ rspec-core (2.13.1)
24
+ rspec-expectations (2.13.0)
25
+ diff-lcs (>= 1.1.3, < 2.0)
26
+ rspec-given (2.4.1)
27
+ rspec (>= 2.11)
28
+ sorcerer (>= 0.3.7)
29
+ rspec-mocks (2.13.1)
30
+ rspec-spies (2.1.4)
31
+ rspec (~> 2.0)
32
+ sidekiq (3.0.0)
33
+ celluloid (>= 0.15.2)
34
+ connection_pool (>= 2.0.0)
35
+ json
36
+ redis (>= 3.0.6)
37
+ redis-namespace (>= 1.3.1)
38
+ simplecov (0.7.1)
39
+ multi_json (~> 1.0)
40
+ simplecov-html (~> 0.7.1)
41
+ simplecov-html (0.7.1)
42
+ sorcerer (0.3.10)
43
+ timers (1.1.0)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ event_bg_bus!
50
+ json
51
+ rake (~> 10.0.1)
52
+ rspec (~> 2.12)
53
+ rspec-given
54
+ rspec-spies
55
+ sidekiq
56
+ simplecov
data/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # EventBus
2
+
3
+ A simple pubsub event bus for Ruby applications.
4
+
5
+ [![Build Status](https://travis-ci.org/artmees/event_bus.svg?branch=master)](https://travis-ci.org/artmees/event_bus)
6
+
7
+ * Source code: <https://github.com/artmees/event_bus>
8
+
9
+ ## Features
10
+
11
+ * Simple, global support for the Observer pattern, aka Publisher-Subscriber.
12
+ * Publish and subscribe to events throughout your Ruby application.
13
+ * Listen for events without coupling to the publishing object or class.
14
+ * Subscribe to events using names or regex patterns.
15
+ * Works with Rails.
16
+ * Works without Rails.
17
+ * Works with Sidekiq.
18
+
19
+ ## Installation
20
+
21
+ Add it to your Gemfile and run `bundle`.
22
+
23
+ ``` ruby
24
+ gem 'event_bus', github: 'artmees/event_bus'
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Publishing events
30
+
31
+ Publish events whenever something significant happens in your application:
32
+
33
+ ```ruby
34
+ class PlaceOrder
35
+ //...
36
+ EventBus.announce(:order_placed, order: current_order, customer: current_user)
37
+ end
38
+ ```
39
+
40
+ ### Publishing background events using sidekiq
41
+
42
+ Publish events whenever something significant happens in your application:
43
+
44
+ ```ruby
45
+ class PlaceOrder
46
+ //...
47
+ EventBGBus.announce(:order_placed, order: current_order, customer: current_user)
48
+ end
49
+ ```
50
+
51
+ The event name (first argument) can be a String or a Symbol.
52
+ The Hash is optional and supplies a payload of information to any subscribers.
53
+
54
+ (If you don't like the method name `announce` you can use `publish` or
55
+ `broadcast` instead.)
56
+
57
+ ### Subscribing to events
58
+
59
+ There are three ways to subscribe to events.
60
+
61
+ 1. Subscribe a listener object:
62
+
63
+ ```ruby
64
+ EventBus.subscribe(StatsRecorder.new)
65
+ ```
66
+
67
+ The event will be handled by a method whose name matches the event name:
68
+
69
+ ```ruby
70
+ class StatsRecorder
71
+ def order_placed(payload)
72
+ order = payload[:order]
73
+ //...
74
+ end
75
+ end
76
+ ```
77
+
78
+ If the object has no matching method, it doesn't receive the event.
79
+
80
+ 2. Specify the method to be called when the event fires:
81
+
82
+ ```ruby
83
+ EventBus.subscribe(:order_placed, StatsRecorder.new, :print_order)
84
+ ```
85
+
86
+ In this case the event will be handled by the `print_order` method:
87
+
88
+ ```ruby
89
+ class StatsRecorder
90
+ def print_order(payload)
91
+ order = payload[:order]
92
+ //...
93
+ end
94
+ end
95
+ ```
96
+
97
+ The first argument to `subscribe` can be a String,
98
+ a Symbol or a Regexp:
99
+
100
+ ```ruby
101
+ EventBus.subscribe(/order/, StatsRecorder.new, :print_order)
102
+ ```
103
+
104
+ 3. Subscribe a block:
105
+
106
+ ```ruby
107
+ EventBus.subscribe(:order_placed) do |payload|
108
+ order = payload[:order]
109
+ //...
110
+ end
111
+ ```
112
+
113
+ The argument to `subscribe` can be a String, a Symbol or a Regexp:
114
+
115
+ ```ruby
116
+ EventBus.subscribe(/order/) do |payload|
117
+ order = payload[:order]
118
+ //...
119
+ end
120
+ ```
121
+
122
+ #### Subscribe to background published events
123
+
124
+ due to a limitation using [Sidekiq](https://github.com/mperham/sidekiq),
125
+ you can not subscribe to events published using `bg_announce` using Symbols in either `event_name` or `payload`.
126
+ you can via Reqexp, Strings or listener objects.
127
+
128
+
129
+ ```ruby
130
+ EventBus.subscribe('order_placed', StatsRecorder.new, :print_order)
131
+ ```
132
+
133
+ ```ruby
134
+ EventBus.subscribe('order_placed') do |payload|
135
+ order = payload['order']
136
+ //...
137
+ end
138
+ ```
139
+
140
+ See the specs for more detailed usage scenarios.
141
+
142
+ ## Compatibility
143
+
144
+ Tested with Ruby 1.9.x, 2.x.x
145
+ See the [build status](https://travis-ci.org/artmees/event_bus)
146
+ for details.
147
+
148
+ ## License
149
+
150
+ (The MIT License)
151
+
152
+ Copyright (c) 2013 Kevin Rutherford
153
+
154
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
155
+ this software and associated documentation files (the 'Software'), to deal in
156
+ the Software without restriction, including without limitation the rights to
157
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
158
+ of the Software, and to permit persons to whom the Software is furnished to do
159
+ so, subject to the following conditions:
160
+
161
+ The above copyright notice and this permission notice shall be included in all
162
+ copies or substantial portions of the Software.
163
+
164
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
165
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
166
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
167
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
168
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
169
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
170
+ SOFTWARE.
171
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new('spec')
4
+
5
+ task :default => :spec
6
+
@@ -0,0 +1,32 @@
1
+ require_relative 'event_bus/event_worker'
2
+ require_relative 'event_bus'
3
+
4
+ class EventBGBus
5
+
6
+ class << self
7
+ #
8
+ # Announce an event in the background 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
+ EventWorker.perform_async(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
+ end
31
+
32
+ end
@@ -0,0 +1,11 @@
1
+ require 'sidekiq'
2
+ require_relative 'registrations'
3
+
4
+ class EventWorker
5
+ include Sidekiq::Worker
6
+
7
+ def perform(event_name, payload)
8
+ # p event_name, payload
9
+ EventBus::Registrations.instance.announce(event_name, payload)
10
+ end
11
+ end
@@ -0,0 +1,74 @@
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
+ private
34
+
35
+ def listeners
36
+ @listeners ||= []
37
+ end
38
+
39
+ def error_handler
40
+ @error_handler
41
+ end
42
+
43
+ def pass_event_to(listener, event_name, payload)
44
+ begin
45
+ listener.respond(event_name, payload)
46
+ rescue => error
47
+ error_handler.call(listener.receiver, payload.merge(error: error)) if error_handler
48
+ end
49
+ end
50
+
51
+ Registration = Struct.new(:pattern, :listener, :method_name) do
52
+ def respond(event_name, payload)
53
+ listener.send(method_name, payload) if pattern === event_name or pattern === event_name.to_sym
54
+ end
55
+
56
+ def receiver
57
+ listener
58
+ end
59
+ end
60
+
61
+ BlockRegistration = Struct.new(:pattern, :block) do
62
+ def respond(event_name, payload)
63
+ block.call(payload) if pattern === event_name
64
+ end
65
+
66
+ def receiver
67
+ block
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
data/lib/event_bus.rb ADDED
@@ -0,0 +1,124 @@
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
+ private
98
+
99
+ def subscribe_pattern(pattern, listener, method_name, &blk)
100
+ if listener
101
+ raise ArgumentError.new('You cannot give both a listener and a block') if block_given?
102
+ raise ArgumentError.new('You must supply a method name') unless method_name
103
+ registrations.add_method(pattern, listener, method_name)
104
+ else
105
+ raise ArgumentError.new('You must provide a listener or a block') unless block_given?
106
+ registrations.add_block(pattern, &blk)
107
+ end
108
+ end
109
+
110
+ def subscribe_obj(listener)
111
+ registrations.add_block(/.*/) do |payload|
112
+ method = payload[:event_name].to_sym
113
+ listener.send(method, payload) if listener.respond_to?(method)
114
+ end
115
+ end
116
+
117
+ def registrations
118
+ Registrations.instance
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
@@ -0,0 +1,224 @@
1
+ require 'spec_helper'
2
+
3
+ describe EventBGBus do
4
+ let(:listener) { double(:listener, handler: true) }
5
+
6
+ before do
7
+ EventBus.clear
8
+ end
9
+
10
+ describe 'publishing' do
11
+
12
+ context 'accepts a string for the event name' do
13
+ Given { EventBus.subscribe(/aa123bb/, listener, :handler) }
14
+ When { EventBGBus.publish('aa123bb') }
15
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
16
+ end
17
+
18
+ context 'accepts a string for the event name (originaly sympol)' do
19
+ Given { EventBus.subscribe(/aa123bb/, listener, :handler) }
20
+ When { EventBGBus.publish(:aa123bb) }
21
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
22
+ end
23
+
24
+ context 'rejects any other type as the event name' do
25
+ When(:result) { EventBGBus.publish(123) }
26
+ Then { result.should have_failed(ArgumentError) }
27
+ end
28
+
29
+ context 'adds the event name to the payload' do
30
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
31
+ When { EventBGBus.publish('aa123bb', a: 56) }
32
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb', 'a' => 56) }
33
+ end
34
+
35
+ context 'convert payload symbol keys to strings' do
36
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
37
+ When { EventBGBus.publish('aa123bb', a: 56) }
38
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb', 'a' => 56) }
39
+ end
40
+
41
+ context 'allows the payload to be omitted' do
42
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
43
+ When { EventBGBus.publish('aa123bb') }
44
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
45
+ end
46
+
47
+ end
48
+
49
+ describe 'EventBGBus publishing with errors' do
50
+ Given(:error) { RuntimeError.new }
51
+ Given(:erroring_listener) { double(:erroring_listener) }
52
+ Given(:error_handler) { double(:error_handler, handle_error: true) }
53
+ Given { erroring_listener.stub(:handler) { raise error } }
54
+
55
+ context 'sends the event to the second listener when the first errors' do
56
+ Given { EventBus.subscribe('aa123bb', erroring_listener, :handler) }
57
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
58
+ When { EventBGBus.publish('aa123bb') }
59
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
60
+ end
61
+
62
+ context 'with an error handler' do
63
+ Given { EventBus.on_error do |listener, payload|
64
+ error_handler.handle_error(listener, payload)
65
+ end }
66
+
67
+ context 'when the listener is an object' do
68
+ Given { EventBus.subscribe('aa123bb', erroring_listener, :handler) }
69
+ When { EventBGBus.publish('aa123bb') }
70
+ Then { error_handler.should have_received(:handle_error).with(erroring_listener, event_name: 'aa123bb', error: error ) }
71
+ end
72
+
73
+ context 'when the listener is a block' do
74
+ Given { EventBus.subscribe('aa123bb') {|info| raise error } }
75
+ When { EventBGBus.publish('aa123bb') }
76
+ Then { error_handler.should have_received(:handle_error).with(instance_of(Proc), event_name: 'aa123bb', error: error) }
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ describe 'subscribing' do
84
+
85
+ context 'EventBGBus publishing' do
86
+ context 'with a regex pattern' do
87
+ context 'sends the event to a matching listener' do
88
+ Given { EventBus.subscribe(/123b/, listener, :handler) }
89
+ When { EventBGBus.publish('aa123bb', a: 1, b: 2) }
90
+ Then { listener.should have_received(:handler).with('a' => 1, 'b' => 2, event_name: 'aa123bb') }
91
+ end
92
+
93
+ context 'does not send the event to non-matching listeners' do
94
+ Given { EventBus.subscribe(/123a/, listener, :handler) }
95
+ When { EventBGBus.publish('aa123bb', 'a' => 1, 'b' => 2, event_name: 'aa123bb') }
96
+ Then { listener.should_not have_received(:handler) }
97
+ end
98
+ end
99
+
100
+ context 'with a string pattern' do
101
+ context 'sends the event to a matching listener' do
102
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
103
+ When { EventBGBus.publish('aa123bb', a: 1, b: 2) }
104
+ Then { listener.should have_received(:handler).with('a' => 1, 'b' => 2, event_name: 'aa123bb') }
105
+ end
106
+
107
+ context 'does not send the event to non-matching listeners' do
108
+ Given { EventBus.subscribe('blah', listener, :handler) }
109
+ When { EventBGBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') }
110
+ Then { listener.should_not have_received(:handler) }
111
+ end
112
+ end
113
+
114
+ context 'with a symbol pattern' do
115
+ context 'sends the event to a matching listener' do
116
+ Given { EventBus.subscribe(:aa123bb, listener, :handler) }
117
+ When { EventBGBus.publish(:aa123bb, a: 1, b: 2) }
118
+ Then { listener.should have_received(:handler).with('a' => 1, 'b' => 2, event_name: 'aa123bb') }
119
+ end
120
+
121
+ context 'does not send the event to non-matching listeners' do
122
+ Given { EventBus.subscribe(:blah, listener, :handler) }
123
+ When { EventBGBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') }
124
+ Then { listener.should_not have_received(:handler) }
125
+ end
126
+ end
127
+
128
+ context 'subscribing a block' do
129
+ Given(:spy) { double(:spy, block_called: nil) }
130
+ Given {
131
+ EventBus.subscribe('aa123bb') {|info| spy.block_called(info) }
132
+ }
133
+
134
+ context 'calls the block when the event matches' do
135
+ When { EventBGBus.publish('aa123bb', a: 1, b: 2) }
136
+ Then { spy.should have_received(:block_called).with('a' => 1, 'b' => 2, event_name: 'aa123bb') }
137
+ end
138
+
139
+ context 'does not call the block when the event does not match' do
140
+ When { EventBGBus.publish('blah') }
141
+ Then { spy.should_not have_received(:block_called) }
142
+ end
143
+ end
144
+
145
+ context 'with a listener object' do
146
+ Given { EventBus.subscribe(listener) }
147
+
148
+ context 'calls a listener method whose name matches the event name' do
149
+ When { EventBGBus.publish('handler', a: 2, b: 3) }
150
+ Then { listener.should have_received(:handler).with('a' => 2, 'b' => 3, event_name: 'handler') }
151
+ end
152
+
153
+ context 'calls a listener method with symbol whose name matches the event name' do
154
+ When { EventBGBus.publish(:handler, a: 2, b: 3) }
155
+ Then { listener.should have_received(:handler).with('a' =>2, 'b' => 3, event_name: 'handler') }
156
+ end
157
+
158
+ context 'calls no method when there is no name match' do
159
+ When { EventBGBus.publish('b_method') }
160
+ Then { listener.should_not have_received(:handler) }
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ context 'when called incorrectly' do
167
+
168
+ context 'when specifying the event name' do
169
+
170
+ context 'must provide a method or a block' do
171
+ When(:subscribe) { EventBus.subscribe('blah', listener) }
172
+ Then { subscribe.should have_failed(ArgumentError) }
173
+ end
174
+
175
+ context 'cannot provide a method AND a block' do
176
+ When(:subscribe) { EventBus.subscribe('blah', listener, :handler) {|info| }}
177
+ Then { subscribe.should have_failed(ArgumentError) }
178
+ end
179
+
180
+ context 'must provide a block when no method is supplied' do
181
+ When(:subscribe) { EventBus.subscribe('blah') }
182
+ Then { subscribe.should have_failed(ArgumentError) }
183
+ end
184
+
185
+ end
186
+
187
+ context 'when specifying a listener object' do
188
+
189
+ context 'when a method is also provided' do
190
+ When(:subscribe) { EventBus.subscribe(listener, double) }
191
+ Then { subscribe.should have_failed(ArgumentError) }
192
+ end
193
+
194
+ context 'when a block is also provided' do
195
+ When(:subscribe) { EventBus.subscribe(listener) {|info| } }
196
+ Then { subscribe.should have_failed(ArgumentError) }
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+
203
+ end
204
+
205
+ describe '.clear' do
206
+ context 'removes all previous registrants' do
207
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
208
+ Given { EventBus.clear }
209
+ When { EventBGBus.publish('aa123bb', {}) }
210
+ Then { listener.should_not have_received(:handler) }
211
+ end
212
+
213
+ end
214
+
215
+ context 'EventBGBus methods cascade' do
216
+
217
+ context 'publish' do
218
+ When(:result) { EventBGBus.publish('aa123bb', {}) }
219
+ Then { result.should == EventBGBus }
220
+ end
221
+
222
+ end
223
+
224
+ end
@@ -0,0 +1,230 @@
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 'publishing' do
11
+
12
+ context 'accepts a string for the event name' do
13
+ Given { EventBus.subscribe(/aa123bb/, listener, :handler) }
14
+ When { EventBus.publish('aa123bb') }
15
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
16
+ end
17
+
18
+ context 'accepts a symbol for the event name' do
19
+ Given { EventBus.subscribe(/aa123bb/, listener, :handler) }
20
+ When { EventBus.publish(:aa123bb) }
21
+ Then { listener.should have_received(:handler).with(event_name: :aa123bb) }
22
+ end
23
+
24
+ context 'rejects any other type as the event name' do
25
+ When(:result) { EventBus.publish(123) }
26
+ Then { result.should have_failed(ArgumentError) }
27
+ end
28
+
29
+ context 'adds the event name to the payload' do
30
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
31
+ When { EventBus.publish('aa123bb', a: 56) }
32
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb', a: 56) }
33
+ end
34
+
35
+ context 'allows the payload to be omitted' do
36
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
37
+ When { EventBus.publish('aa123bb') }
38
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
39
+ end
40
+
41
+ end
42
+
43
+ describe 'publishing with errors' do
44
+ Given(:error) { RuntimeError.new }
45
+ Given(:erroring_listener) { double(:erroring_listener) }
46
+ Given(:error_handler) { double(:error_handler, handle_error: true) }
47
+ Given { erroring_listener.stub(:handler) { raise error } }
48
+
49
+ context 'sends the event to the second listener when the first errors' do
50
+ Given { EventBus.subscribe('aa123bb', erroring_listener, :handler) }
51
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
52
+ When { EventBus.publish('aa123bb') }
53
+ Then { listener.should have_received(:handler).with(event_name: 'aa123bb') }
54
+ end
55
+
56
+ context 'with an error handler' do
57
+ Given { EventBus.on_error do |listener, payload|
58
+ error_handler.handle_error(listener, payload)
59
+ end }
60
+
61
+ context 'when the listener is an object' do
62
+ Given { EventBus.subscribe('aa123bb', erroring_listener, :handler) }
63
+ When { EventBus.publish('aa123bb') }
64
+ Then { error_handler.should have_received(:handle_error).with(erroring_listener, event_name: 'aa123bb', error: error ) }
65
+ end
66
+
67
+ context 'when the listener is a block' do
68
+ Given { EventBus.subscribe('aa123bb') {|info| raise error } }
69
+ When { EventBus.publish('aa123bb') }
70
+ Then { error_handler.should have_received(:handle_error).with(instance_of(Proc), event_name: 'aa123bb', error: error) }
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ describe 'subscribing' do
78
+
79
+ context 'publishing' do
80
+ context 'with a regex pattern' do
81
+ context 'sends the event to a matching listener' do
82
+ Given { EventBus.subscribe(/123b/, listener, :handler) }
83
+ When { EventBus.publish('aa123bb', a: 1, b: 2) }
84
+ Then { listener.should have_received(:handler).with(a: 1, b: 2, event_name: 'aa123bb') }
85
+ end
86
+
87
+ context 'does not send the event to non-matching listeners' do
88
+ Given { EventBus.subscribe(/123a/, listener, :handler) }
89
+ When { EventBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') }
90
+ Then { listener.should_not have_received(:handler) }
91
+ end
92
+ end
93
+
94
+ context 'with a string pattern' do
95
+ context 'sends the event to a matching listener' do
96
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
97
+ When { EventBus.publish('aa123bb', a: 1, b: 2) }
98
+ Then { listener.should have_received(:handler).with(a: 1, b: 2, event_name: 'aa123bb') }
99
+ end
100
+
101
+ context 'does not send the event to non-matching listeners' do
102
+ Given { EventBus.subscribe('blah', listener, :handler) }
103
+ When { EventBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') }
104
+ Then { listener.should_not have_received(:handler) }
105
+ end
106
+ end
107
+
108
+ context 'with a symbol pattern' do
109
+ context 'sends the event to a matching listener' do
110
+ Given { EventBus.subscribe(:aa123bb, listener, :handler) }
111
+ When { EventBus.publish(:aa123bb, a: 1, b: 2) }
112
+ Then { listener.should have_received(:handler).with(a: 1, b: 2, event_name: :aa123bb) }
113
+ end
114
+
115
+ context 'does not send the event to non-matching listeners' do
116
+ Given { EventBus.subscribe(:blah, listener, :handler) }
117
+ When { EventBus.publish('aa123bb', a: 1, b: 2, event_name: 'aa123bb') }
118
+ Then { listener.should_not have_received(:handler) }
119
+ end
120
+ end
121
+
122
+ context 'subscribing a block' do
123
+ Given(:spy) { double(:spy, block_called: nil) }
124
+ Given {
125
+ EventBus.subscribe('aa123bb') {|info| spy.block_called(info) }
126
+ }
127
+
128
+ context 'calls the block when the event matches' do
129
+ When { EventBus.publish('aa123bb', a: 1, b: 2) }
130
+ Then { spy.should have_received(:block_called).with(a: 1, b: 2, event_name: 'aa123bb') }
131
+ end
132
+
133
+ context 'does not call the block when the event does not match' do
134
+ When { EventBus.publish('blah') }
135
+ Then { spy.should_not have_received(:block_called) }
136
+ end
137
+ end
138
+
139
+ context 'with a listener object' do
140
+ Given { EventBus.subscribe(listener) }
141
+
142
+ context 'calls a listener method whose name matches the event name' do
143
+ When { EventBus.publish('handler', a: 2, b: 3) }
144
+ Then { listener.should have_received(:handler).with(a: 2, b: 3, event_name: 'handler') }
145
+ end
146
+
147
+ context 'calls a listener method with symbol whose name matches the event name' do
148
+ When { EventBus.publish(:handler, a: 2, b: 3) }
149
+ Then { listener.should have_received(:handler).with(a: 2, b: 3, event_name: :handler) }
150
+ end
151
+
152
+ context 'calls no method when there is no name match' do
153
+ When { EventBus.publish('b_method') }
154
+ Then { listener.should_not have_received(:handler) }
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
161
+ context 'when called incorrectly' do
162
+
163
+ context 'when specifying the event name' do
164
+
165
+ context 'must provide a method or a block' do
166
+ When(:subscribe) { EventBus.subscribe('blah', listener) }
167
+ Then { subscribe.should have_failed(ArgumentError) }
168
+ end
169
+
170
+ context 'cannot provide a method AND a block' do
171
+ When(:subscribe) { EventBus.subscribe('blah', listener, :handler) {|info| }}
172
+ Then { subscribe.should have_failed(ArgumentError) }
173
+ end
174
+
175
+ context 'must provide a block when no method is supplied' do
176
+ When(:subscribe) { EventBus.subscribe('blah') }
177
+ Then { subscribe.should have_failed(ArgumentError) }
178
+ end
179
+
180
+ end
181
+
182
+ context 'when specifying a listener object' do
183
+
184
+ context 'when a method is also provided' do
185
+ When(:subscribe) { EventBus.subscribe(listener, double) }
186
+ Then { subscribe.should have_failed(ArgumentError) }
187
+ end
188
+
189
+ context 'when a block is also provided' do
190
+ When(:subscribe) { EventBus.subscribe(listener) {|info| } }
191
+ Then { subscribe.should have_failed(ArgumentError) }
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+
200
+ describe '.clear' do
201
+ context 'removes all previous registrants' do
202
+ Given { EventBus.subscribe('aa123bb', listener, :handler) }
203
+ Given { EventBus.clear }
204
+ When { EventBus.publish('aa123bb', {}) }
205
+ Then { listener.should_not have_received(:handler) }
206
+ end
207
+
208
+ end
209
+
210
+ context 'EventBus methods cascade' do
211
+
212
+ context 'clear' do
213
+ When(:result) { EventBus.clear }
214
+ Then { result.should == EventBus }
215
+ end
216
+
217
+ context 'publish' do
218
+ When(:result) { EventBus.publish('aa123bb', {}) }
219
+ Then { result.should == EventBus }
220
+ end
221
+
222
+ context 'subscribe' do
223
+ When(:result) { EventBus.subscribe('aa123bb', listener, :handler) }
224
+ Then { result.should == EventBus }
225
+ end
226
+
227
+ end
228
+
229
+ end
230
+
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'simplecov'
4
+ require 'rspec-spies'
5
+ require 'rspec-given'
6
+ require 'sidekiq'
7
+ require 'sidekiq/testing'
8
+
9
+ Sidekiq::Testing.inline!
10
+ SimpleCov.start
11
+
12
+ require 'event_bus'
13
+ require 'event_bg_bus'
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: event_bg_bus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Rutherford
8
+ - Ahmed Abdel Razzak
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '10.0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '10.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '2.12'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '2.12'
42
+ - !ruby/object:Gem::Dependency
43
+ name: simplecov
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: sidekiq
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ description: event_bus provides support for application-wide events, without coupling
71
+ the publishing and subscribing objects or classes to each other
72
+ email:
73
+ - kevin@rutherford-software.com
74
+ - eng.abdelrazzak@gmail.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - .rspec
80
+ - .yardopts
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - README.md
84
+ - Rakefile
85
+ - lib/event_bg_bus.rb
86
+ - lib/event_bus.rb
87
+ - lib/event_bus/event_worker.rb
88
+ - lib/event_bus/registrations.rb
89
+ - spec/lib/event_bg_bus_spec.rb
90
+ - spec/lib/event_bus_spec.rb
91
+ - spec/spec_helper.rb
92
+ homepage: http://github.com/artmees/event_bus
93
+ licenses: []
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.2.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: A simple pubsub event bus for Ruby applications with sidekiq background support
115
+ test_files:
116
+ - spec/lib/event_bg_bus_spec.rb
117
+ - spec/lib/event_bus_spec.rb
118
+ - spec/spec_helper.rb