event_bg_bus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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