event_bus 0.0.4 → 0.1.0

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.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
4
  group :test do
data/Gemfile.lock CHANGED
@@ -1,22 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- event_bus (0.0.3)
4
+ event_bus (0.0.4)
5
5
 
6
6
  GEM
7
- remote: http://rubygems.org/
7
+ remote: https://rubygems.org/
8
8
  specs:
9
- diff-lcs (1.1.3)
10
- multi_json (1.5.0)
11
- rake (10.0.3)
12
- rspec (2.12.0)
13
- rspec-core (~> 2.12.0)
14
- rspec-expectations (~> 2.12.0)
15
- rspec-mocks (~> 2.12.0)
16
- rspec-core (2.12.2)
17
- rspec-expectations (2.12.1)
18
- diff-lcs (~> 1.1.3)
19
- rspec-mocks (2.12.1)
9
+ diff-lcs (1.2.3)
10
+ multi_json (1.7.2)
11
+ rake (10.0.4)
12
+ rspec (2.13.0)
13
+ rspec-core (~> 2.13.0)
14
+ rspec-expectations (~> 2.13.0)
15
+ rspec-mocks (~> 2.13.0)
16
+ rspec-core (2.13.1)
17
+ rspec-expectations (2.13.0)
18
+ diff-lcs (>= 1.1.3, < 2.0)
19
+ rspec-mocks (2.13.1)
20
20
  simplecov (0.7.1)
21
21
  multi_json (~> 1.0)
22
22
  simplecov-html (~> 0.7.1)
data/README.md CHANGED
@@ -5,10 +5,12 @@ A simple pubsub event bus for Ruby applications.
5
5
  [![Build Status](https://travis-ci.org/kevinrutherford/event_bus.png)](https://travis-ci.org/kevinrutherford/event_bus)
6
6
  [![Dependency
7
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)
8
10
 
9
- * <https://rubygems.org/gems/event_bus>
10
- * <http://rubydoc.info/gems/event_bus/frames>
11
- * <https://github.com/kevinrutherford/event_bus>
11
+ * Gem: <https://rubygems.org/gems/event_bus>
12
+ * API docs: <http://rubydoc.info/gems/event_bus/frames>
13
+ * Source code: <https://github.com/kevinrutherford/event_bus>
12
14
 
13
15
  ## Features
14
16
 
@@ -17,6 +19,7 @@ Status](https://gemnasium.com/kevinrutherford/event_bus.png)](https://gemnasium.
17
19
  * Listen for events without coupling to the publishing object or class.
18
20
  * Subscribe to events using names or regex patterns.
19
21
  * Works with Rails.
22
+ * Works without Rails.
20
23
 
21
24
  ## Installation
22
25
 
@@ -34,44 +37,93 @@ gem 'event_bus'
34
37
 
35
38
  ## Usage
36
39
 
37
- Subscribe to an event in your application's initialization code:
40
+ ### Publishing events
38
41
 
39
- ```ruby
40
- EventBus.subscribe('order-placed', StatsRecorder.new, :order_placed)
41
- ```
42
+ Publish events whenever something significant happens in your application:
42
43
 
43
44
  ```ruby
44
- class StatsRecorder
45
- def order_placed(details)
46
- order = details[:order]
47
- //...
48
- end
45
+ class PlaceOrder
46
+ //...
47
+ EventBus.announce(:order_placed, order: current_order, customer: current_user)
49
48
  end
50
49
  ```
51
50
 
52
- Or subscribe a block:
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
53
 
54
- ```ruby
55
- EventBus.subscribe('order-placed') do |details|
56
- order = details[:order]
57
- //...
58
- end
59
- ```
54
+ (If you don't like the method name `announce` you can use `publish` or
55
+ `broadcast` instead.)
60
56
 
61
- Fire the event whenever something significant happens in your application:
57
+ ### Subscribing to events
62
58
 
63
- ```ruby
64
- class PlaceOrder
65
- //...
66
- EventBus.announce('order-placed', :order => current_order, :customer => current_user)
67
- end
68
- ```
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
+ ```
69
121
 
70
122
  See the specs for more detailed usage scenarios.
71
123
 
72
124
  ## Compatibility
73
125
 
74
- Tested with Ruby 1.8.7, 1.9.x, JRuby, Rubinius.
126
+ Tested with Ruby 1.9.x, JRuby, Rubinius.
75
127
  See the [build status](https://travis-ci.org/kevinrutherford/event_bus)
76
128
  for details.
77
129
 
data/lib/event_bus.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'singleton'
1
+ require_relative 'event_bus/registrations'
2
2
 
3
3
  class EventBus
4
4
 
@@ -7,37 +7,61 @@ class EventBus
7
7
  #
8
8
  # Announce an event to any waiting listeners.
9
9
  #
10
- # The +event_name+ is added to the +details+ hash (with the key +:event_name+)
11
- # before the event is passed on to listeners.
10
+ # The +event_name+ is added to the +payload+ hash (with the key +:event_name+)
11
+ # before being passed on to listeners.
12
12
  #
13
13
  # @param event_name [String, Symbol] the name of your event
14
- # @param details [Hash] the information you want to pass to the listeners
15
- # @return the EventBus, ready to be called again.
14
+ # @param payload [Hash] the information you want to pass to the listeners
15
+ # @return [EventBus] the EventBus, ready to be called again.
16
16
  #
17
- def publish(event_name, details = {})
18
- registrations.announce(event_name, details)
19
- self
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
20
25
  end
21
26
 
22
27
  alias :announce :publish
23
28
  alias :broadcast :publish
24
29
 
25
30
  #
26
- # Register a single listener.
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.
27
42
  #
28
- # @param pattern [String, Regex] listen for any events whose name matches this pattern
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
29
53
  # @param listener the object to be notified when a matching event occurs
30
54
  # @param method_name [Symbol] the method to be called on +listener+ when a matching event occurs
31
- # @return the EventBus, ready to be called again.
55
+ # @return [EventBus] the EventBus, ready to be called again.
32
56
  #
33
57
  def subscribe(pattern, listener = nil, method_name = nil, &blk)
34
- if listener
35
- raise ArgumentError.new('You cannot give both a listener and a block') if block_given?
36
- raise ArgumentError.new('You must supply a method name') unless method_name
37
- registrations.add_method(pattern, listener, method_name)
58
+ case pattern
59
+ when Regexp, String
60
+ subscribe_pattern(pattern, listener, method_name, &blk)
38
61
  else
39
- raise ArgumentError.new('You must provide a listener or a block') unless block_given?
40
- registrations.add_block(pattern, blk)
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)
41
65
  end
42
66
  self
43
67
  end
@@ -66,58 +90,26 @@ class EventBus
66
90
 
67
91
  private
68
92
 
69
- def registrations
70
- Registrations.instance
71
- end
72
-
73
- end
74
-
75
- class Registrations
76
- include Singleton
77
-
78
- def initialize
79
- clear
80
- end
81
-
82
- def announce(event_name, details)
83
- info = {:event_name => event_name}.merge(details)
84
- @listeners.each do |listener|
85
- listener.respond(event_name, info)
86
- end
87
- end
88
-
89
- def clear
90
- @listeners = []
91
- end
92
-
93
- def add(pattern, listener, method_name, &blk)
93
+ def subscribe_pattern(pattern, listener, method_name, &blk)
94
94
  if listener
95
- add_method(pattern, listener, method_name)
95
+ raise ArgumentError.new('You cannot give both a listener and a block') if block_given?
96
+ raise ArgumentError.new('You must supply a method name') unless method_name
97
+ registrations.add_method(pattern, listener, method_name)
96
98
  else
97
- add_block(pattern, blk)
99
+ raise ArgumentError.new('You must provide a listener or a block') unless block_given?
100
+ registrations.add_block(pattern, &blk)
98
101
  end
99
102
  end
100
103
 
101
- def add_method(pattern, listener, method_name)
102
- @listeners << Registration.new(pattern, listener, method_name)
103
- end
104
-
105
- def add_block(pattern, blk)
106
- @listeners << BlockRegistration.new(pattern, blk)
107
- end
108
-
109
- private
110
-
111
- Registration = Struct.new(:pattern, :listener, :method_name) do
112
- def respond(event_name, details)
113
- listener.send(method_name, details) if pattern === event_name
104
+ def subscribe_obj(listener)
105
+ registrations.add_block(/.*/) do |payload|
106
+ method = payload[:event_name].to_sym
107
+ listener.send(method, payload) if listener.respond_to?(method)
114
108
  end
115
109
  end
116
110
 
117
- BlockRegistration = Struct.new(:pattern, :block) do
118
- def respond(event_name, details)
119
- block.call(details) if pattern === event_name
120
- end
111
+ def registrations
112
+ Registrations.instance
121
113
  end
122
114
 
123
115
  end
@@ -0,0 +1,58 @@
1
+ require 'singleton'
2
+
3
+ class EventBus
4
+
5
+ private
6
+
7
+ class Registrations
8
+ include Singleton
9
+
10
+ def initialize
11
+ clear
12
+ end
13
+
14
+ def announce(event_name, payload)
15
+ full_payload = {event_name: event_name}.merge(payload)
16
+ @listeners.each do |listener|
17
+ listener.respond(event_name, full_payload)
18
+ end
19
+ end
20
+
21
+ def clear
22
+ @listeners = []
23
+ end
24
+
25
+ def add(pattern, listener, method_name, &blk)
26
+ if listener
27
+ add_method(pattern, listener, method_name)
28
+ else
29
+ add_block(pattern, blk)
30
+ end
31
+ end
32
+
33
+ def add_method(pattern, listener, method_name)
34
+ @listeners << Registration.new(pattern, listener, method_name)
35
+ end
36
+
37
+ def add_block(pattern, &blk)
38
+ @listeners << BlockRegistration.new(pattern, blk)
39
+ end
40
+
41
+ private
42
+
43
+ Registration = Struct.new(:pattern, :listener, :method_name) do
44
+ def respond(event_name, payload)
45
+ listener.send(method_name, payload) if pattern === event_name
46
+ end
47
+ end
48
+
49
+ BlockRegistration = Struct.new(:pattern, :block) do
50
+ def respond(event_name, payload)
51
+ block.call(payload) if pattern === event_name
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
@@ -9,93 +9,138 @@ describe EventBus do
9
9
  EventBus.clear
10
10
  end
11
11
 
12
- describe '.publish' do
12
+ describe 'publishing' do
13
+
14
+ it 'accepts a string for the event name' do
15
+ EventBus.subscribe(/#{event_name}/, listener, receiving_method)
16
+ listener.should_receive(receiving_method).with(event_name: event_name)
17
+ EventBus.publish(event_name)
18
+ end
19
+
20
+ it 'accepts a symbol for the event name' do
21
+ event_sym = :abc_123
22
+ EventBus.subscribe(/#{event_sym}/, listener, receiving_method)
23
+ listener.should_receive(receiving_method).with(event_name: event_sym)
24
+ EventBus.publish(event_sym)
25
+ end
26
+
27
+ it 'rejects any other type as the event name' do
28
+ expect { EventBus.publish(123) }.to raise_error(ArgumentError)
29
+ end
13
30
 
14
31
  it 'returns itself, to facilitate cascades' do
15
32
  EventBus.publish(event_name, {}).should == EventBus
16
33
  end
17
34
 
18
- it 'passes the event name in the details hash' do
35
+ it 'adds the event name to the payload' do
19
36
  EventBus.subscribe(event_name, listener, receiving_method)
20
- listener.should_receive(receiving_method).with(:event_name => event_name)
21
- EventBus.publish(event_name, {})
37
+ listener.should_receive(receiving_method).with(event_name: event_name, a: 56)
38
+ EventBus.publish(event_name, a: 56)
22
39
  end
23
40
 
24
- it 'allows the details hash to be omitted' do
41
+ it 'allows the payload to be omitted' do
25
42
  EventBus.subscribe(event_name, listener, receiving_method)
26
- listener.should_receive(receiving_method).with(:event_name => event_name)
43
+ listener.should_receive(receiving_method).with(event_name: event_name)
27
44
  EventBus.publish(event_name)
28
45
  end
29
46
 
30
47
  end
31
48
 
32
- describe '.subscribe' do
49
+ describe 'subscribing' do
33
50
 
34
51
  it 'returns itself, to facilitate cascades' do
35
52
  EventBus.subscribe(event_name, listener, receiving_method).should == EventBus
36
53
  end
37
54
 
38
- context 'accepts a string event name' do
55
+ context 'with a regex pattern' do
39
56
  it 'sends the event to a matching listener' do
40
- EventBus.subscribe(event_name, listener, receiving_method)
41
- listener.should_receive(receiving_method).with(:a => 1, :b => 2, :event_name => event_name)
42
- EventBus.publish(event_name, :a => 1, :b => 2)
57
+ EventBus.subscribe(/123b/, listener, receiving_method)
58
+ listener.should_receive(receiving_method).with(a: 1, b: 2, event_name: event_name)
59
+ EventBus.publish(event_name, a: 1, b: 2)
43
60
  end
44
61
 
45
62
  it 'does not send the event to non-matching listeners' do
46
- EventBus.subscribe('blah', listener, receiving_method)
63
+ EventBus.subscribe(/123a/, listener, receiving_method)
47
64
  listener.should_not_receive(receiving_method)
48
- EventBus.publish(event_name, :a => 1, :b => 2, :event_name => event_name)
65
+ EventBus.publish(event_name, a: 1, b: 2, event_name: event_name)
49
66
  end
50
67
  end
51
68
 
52
- context 'accepts a regex event name' do
69
+ context 'with a string pattern' do
53
70
  it 'sends the event to a matching listener' do
54
- EventBus.subscribe(/123b/, listener, receiving_method)
55
- listener.should_receive(receiving_method).with(:a => 1, :b => 2, :event_name => event_name)
56
- EventBus.publish(event_name, :a => 1, :b => 2)
71
+ EventBus.subscribe(event_name, listener, receiving_method)
72
+ listener.should_receive(receiving_method).with(a: 1, b: 2, event_name: event_name)
73
+ EventBus.publish(event_name, a: 1, b: 2)
57
74
  end
58
75
 
59
76
  it 'does not send the event to non-matching listeners' do
60
- EventBus.subscribe(/123a/, listener, receiving_method)
77
+ EventBus.subscribe('blah', listener, receiving_method)
61
78
  listener.should_not_receive(receiving_method)
62
- EventBus.publish(event_name, :a => 1, :b => 2, :event_name => event_name)
79
+ EventBus.publish(event_name, a: 1, b: 2, event_name: event_name)
80
+ end
81
+ end
82
+
83
+ context 'with a listener method' do
84
+ it 'will not accept a block too' do
85
+ expect { EventBus.subscribe('blah', listener, receiving_method) {|info| }}.to raise_error(ArgumentError)
86
+ end
87
+
88
+ it 'expects a method name' do
89
+ expect { EventBus.subscribe('blah', listener) }.to raise_error(ArgumentError)
63
90
  end
64
91
  end
65
92
 
66
- context 'can take a block or a receiver' do
93
+ context 'with a block' do
94
+ it 'requires a block when no listener method is supplied' do
95
+ expect { EventBus.subscribe('blah') }.to raise_error(ArgumentError)
96
+ end
97
+
67
98
  it 'calls the block when the event matches' do
68
99
  block_called = false
69
100
  EventBus.subscribe(event_name) do |info|
70
101
  block_called = true
71
- info.should == {:a => 1, :b => 2, :event_name => event_name}
102
+ info.should == {a: 1, b: 2, event_name: event_name}
72
103
  end
73
- EventBus.publish(event_name, :a => 1, :b => 2)
104
+ EventBus.publish(event_name, a: 1, b: 2)
74
105
  block_called.should be_true
75
106
  end
76
107
 
77
108
  it 'does not call the block when the event does not match' do
78
109
  block_called = false
79
110
  EventBus.subscribe('blah') {|_| block_called = true }
80
- EventBus.publish(event_name, :a => 1, :b => 2)
111
+ EventBus.publish(event_name)
81
112
  block_called.should be_false
82
113
  end
83
114
  end
84
115
 
85
- context 'listener variant' do
86
- it 'will not accept a block too' do
87
- expect { EventBus.subscribe('blah', listener, receiving_method) {|info| }}.to raise_error(ArgumentError)
116
+ context 'with a listener object' do
117
+
118
+ it 'calls a listener method whose name matches the event name' do
119
+ listener.should_receive(:a_method).with(a: 2, b: 3, event_name: 'a_method')
120
+ EventBus.subscribe(listener)
121
+ EventBus.publish('a_method', a: 2, b: 3)
88
122
  end
89
123
 
90
- it 'expects a method name' do
91
- expect { EventBus.subscribe('blah', listener)}.to raise_error(ArgumentError)
124
+ it 'calls a listener method with symbol whose name matches the event name' do
125
+ listener.should_receive(:a_method).with(a: 2, b: 3, event_name: :a_method)
126
+ EventBus.subscribe(listener)
127
+ EventBus.publish(:a_method, a: 2, b: 3)
92
128
  end
93
- end
94
129
 
95
- context 'block variant' do
96
- it 'requires a block when no listener method is supplied' do
97
- expect { EventBus.subscribe('blah')}.to raise_error(ArgumentError)
130
+ it 'calls no method when there is no name match' do
131
+ listener.should_not_receive(:a_method)
132
+ EventBus.subscribe(listener)
133
+ EventBus.publish('b_method')
98
134
  end
135
+
136
+ it 'will not accept other arguments' do
137
+ expect { EventBus.subscribe(listener, double) }.to raise_error(ArgumentError)
138
+ end
139
+
140
+ it 'will not accept a block' do
141
+ expect { EventBus.subscribe(listener) {|info| }}.to raise_error(ArgumentError)
142
+ end
143
+
99
144
  end
100
145
 
101
146
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-27 00:00:00.000000000 Z
12
+ date: 2013-04-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -73,6 +73,7 @@ files:
73
73
  - README.md
74
74
  - Rakefile
75
75
  - lib/event_bus.rb
76
+ - lib/event_bus/registrations.rb
76
77
  - spec/lib/event_bus_spec.rb
77
78
  - spec/spec_helper.rb
78
79
  homepage: http://github.com/kevinrutherford/event_bus