event_aggregator 1.0.0.pre → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +48 -29
- data/event_aggregator.gemspec +1 -1
- data/lib/event_aggregator.rb +2 -0
- data/lib/event_aggregator/aggregator.rb +3 -2
- data/lib/event_aggregator/message.rb +29 -11
- data/lib/event_aggregator/message_job.rb +11 -1
- data/lib/event_aggregator/version.rb +1 -1
- data/spec/lib/event_aggregator/aggregator_spec.rb +6 -6
- data/spec/lib/event_aggregator/message_job_spec.rb +22 -0
- data/spec/spec_helper.rb +2 -2
- metadata +7 -5
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
# EventAggregator
|
1
|
+
# EventAggregator gem
|
2
2
|
|
3
|
-
The gem 'event_aggregator' is designed for
|
4
|
-
|
5
|
-
An event aggregator is essentially a message passing service that aims at decoupeling objects in your code. This is achieved with messages that has a type and some data. A message might be produced when an event, or other condition, occurs within one object or class that might be of interest to thers. This object or class then put all relevant data into the message and publishes it. This message will then be distributed to all other objects that want to recieve this message type. This way the object or class that first got knowledge of the condition do not need to be aware of every other object that might be interested in knowing about this. It also removes the need for this class to implement any listeners and so forth. This way the listener, the receiver of the message, does not need to know that the sender even exists. This will help your code be a lot cleaner and remove a lot of bug-producing couplings between objects.
|
3
|
+
The gem 'event_aggregator' is designed for use with the event aggregator pattern in Ruby.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -36,6 +34,10 @@ Or install it yourself as:
|
|
36
34
|
def handle_message(data)
|
37
35
|
puts data
|
38
36
|
end
|
37
|
+
|
38
|
+
def foo_unregister(*args)
|
39
|
+
message_type_unregister(*args)
|
40
|
+
end
|
39
41
|
end
|
40
42
|
|
41
43
|
f = Foo.new
|
@@ -45,56 +47,67 @@ Or install it yourself as:
|
|
45
47
|
EventAggregator::Message.new("foo2", "data").publish
|
46
48
|
#=> data
|
47
49
|
EventAggregator::Message.new("foo3", "data").publish
|
48
|
-
#=>
|
49
|
-
f.
|
50
|
+
#=> []
|
51
|
+
f.foo_unregister("foo2")
|
50
52
|
EventAggregator::Message.new("foo2", "data").publish
|
51
|
-
#=>
|
53
|
+
#=> []
|
52
54
|
|
53
|
-
|
54
|
-
EventAggregator::Message.new("
|
55
|
+
#Possible outcome:
|
56
|
+
EventAggregator::Message.new("foo", "data").publish
|
57
|
+
EventAggregator::Message.new("foo", "data2").publish
|
55
58
|
#=> data2
|
56
59
|
#=> data
|
57
60
|
|
58
|
-
Message.publish is
|
61
|
+
Message.publish is asynchronous by default. To make it synchronous (not recommended) use the following:
|
59
62
|
|
60
|
-
EventAggregator::Message.new("
|
63
|
+
EventAggregator::Message.new("foo", "data", false).publish
|
61
64
|
#=> data
|
62
65
|
|
63
|
-
The message data is duplicated by default for each of the receiving listeners. To force the same object for all listeners, set the consisten_data property.
|
66
|
+
The message data is duplicated by default for each of the receiving listeners. To force the same object for all listeners, set the consisten_data property to true.
|
64
67
|
|
65
|
-
EventAggregator::Message.new("
|
68
|
+
EventAggregator::Message.new("foo", "data", true, true).publish
|
66
69
|
|
67
|
-
This enables
|
70
|
+
This enables the following:
|
68
71
|
|
69
72
|
class Foo
|
70
73
|
include EventAggregator::Listener
|
71
74
|
def initialize()
|
72
|
-
message_type_register( "foo", lambda{|data| data
|
75
|
+
message_type_register( "foo", lambda{|data| data << " bar" } )
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
76
79
|
f1 = Foo.new
|
77
80
|
f2 = Foo.new
|
78
81
|
data = "foo"
|
82
|
+
|
83
|
+
EventAggregator::Message.new("foo", data, true, false).publish
|
84
|
+
|
85
|
+
puts data
|
86
|
+
#=> "foo"
|
79
87
|
|
80
|
-
EventAggregator::Message.new("foo", data).publish
|
88
|
+
EventAggregator::Message.new("foo", data, true, true).publish
|
81
89
|
|
82
90
|
puts data
|
83
91
|
#=> "foo bar bar"
|
84
92
|
|
93
|
+
EventAggregator::Message.new("foo", data, true, true).publish
|
94
|
+
|
95
|
+
puts data
|
96
|
+
#=> "foo bar bar bar bar"
|
97
|
+
|
98
|
+
|
85
99
|
|
86
100
|
## Usage Considerations
|
87
|
-
All messages are processed
|
101
|
+
All messages are processed asynchronous by default. This means that there might be raise conditions in your code.
|
88
102
|
|
89
|
-
If you force
|
103
|
+
If you force synchronous message publishing you should take extra care of where in your code you produce new messages. You can very easily create infinite loops where messages are published and consumed by the same listener. Because of this it is advised not to produce messages within the callback for the listener, even when using asynchronous message publishing. Another good rule is never to produce messages of the same type as those you listen to. This does not completely guard you, as there can still exist loops between two or more listeners.
|
90
104
|
|
91
|
-
##
|
105
|
+
## About Event Aggregators
|
106
|
+
An event aggregator is essentially a message passing service that aims at decoupling objects in your code. This is achieved with messages that has a type and some data. A message might be produced when an event, or other condition, occurs. When such conditions occurs a message can be produced with all relevant data in it. This message can then be published. The message will then be distributed to all other objects that want to receive this message type. This way the object or class that produced the message do not need to be aware of every other object that might be interested in the condition that just occurred. It also removes the need for this class to implement any consumer producer pattern or other similar methods to solving this problem. With an event aggregator the listener, the receiver of the message, does not need to know that the sender even exists. This will remove a lot of bug-producing couplings between objects and help your code become cleaner.
|
92
107
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
97
|
-
5. Create new Pull Request
|
108
|
+
For more information see: http://martinfowler.com/eaaDev/EventAggregator.html
|
109
|
+
|
110
|
+
Or: https://www.google.com/#q=event+aggregator
|
98
111
|
|
99
112
|
## Todo:
|
100
113
|
- Improving the readme and documentation in the gem.
|
@@ -102,11 +115,17 @@ If you force synchroneus message publishing you should take extra care of where
|
|
102
115
|
## Versioning Standard:
|
103
116
|
Using Semantic Versioning - http://semver.org/
|
104
117
|
### Versioning Summary
|
105
|
-
Given a version number MAJOR.MINOR.PATCH, increment the:
|
106
118
|
|
107
|
-
|
119
|
+
#### 0.0.X - Patch
|
108
120
|
Small updates and patches that are backwards-compatible. Updating from 0.0.X -> 0.0.Y should not break your code.
|
109
|
-
|
121
|
+
#### 0.X - Minor
|
110
122
|
Adding functionality and changes that are backwards-compatible. Updating from 0.X -> 0.Y should not break your code.
|
111
|
-
|
112
|
-
Architectural changes and other major changes that alter the API. Updating from X -> Y will most likely break your code.
|
123
|
+
#### X - Major
|
124
|
+
Architectural changes and other major changes that alter the API. Updating from X -> Y will most likely break your code.
|
125
|
+
## Contributing
|
126
|
+
|
127
|
+
1. Fork it
|
128
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
129
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
130
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
131
|
+
5. Create new Pull Request
|
data/event_aggregator.gemspec
CHANGED
data/lib/event_aggregator.rb
CHANGED
@@ -22,6 +22,7 @@ module EventAggregator
|
|
22
22
|
# message_type - The message type to recieve. Can be anything except nil.
|
23
23
|
# Often it is preferable to use a string eg. "Message Type".
|
24
24
|
def self.register( listener, message_type, callback )
|
25
|
+
raise "Illegal callback" unless callback.respond_to?(:call)
|
25
26
|
@@listeners[message_type] << [listener, callback] unless ! (listener.class < EventAggregator::Listener) || @@listeners[message_type].include?(listener)
|
26
27
|
end
|
27
28
|
|
@@ -52,11 +53,11 @@ module EventAggregator
|
|
52
53
|
# message - The message to be distributed to the listeners.
|
53
54
|
# async - true => message will be sent async. Default true
|
54
55
|
# consisten_data - true => the same object will be sent to all recievers. Default false
|
55
|
-
def self.message_publish ( message
|
56
|
+
def self.message_publish ( message )
|
56
57
|
raise "Invalid message" unless message.respond_to?(:message_type) && message.respond_to?(:data)
|
57
58
|
@@listeners[message.message_type].each do |l|
|
58
59
|
if l[1].respond_to? :call
|
59
|
-
case [async, consisten_data]
|
60
|
+
case [message.async, message.consisten_data]
|
60
61
|
when [true, true] then EventAggregator::MessageJob.new.async.perform(message.data, l[1])
|
61
62
|
when [true, false] then EventAggregator::MessageJob.new.async.perform(message.data.clone, l[1])
|
62
63
|
when [false, true] then EventAggregator::MessageJob.new.perform( message.data, l[1])
|
@@ -1,20 +1,37 @@
|
|
1
1
|
module EventAggregator
|
2
|
+
|
3
|
+
# Public: The Message class is used as a distribution object
|
4
|
+
# for the EventAggregator::Aggregator to send messages to
|
5
|
+
# EventAggregator::Listener objects
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# EventAggregator::Message.new("foo", "data").publish
|
10
|
+
# EventAggregator::Message.new("foo", "data", true, false).publish #equivalent to the first example
|
11
|
+
# EventAggregator::Message.new("foo2", 7843).publish #Data can be anything
|
12
|
+
# #Data can be anything
|
13
|
+
# EventAggregator::Message.new("foo2", lambda{p ["", "bA", "bw", "bA", "IA", "bg", "YQ", "aA", "cA", "ZQ", "dA", "w"].map{|e| e[0] && e[0] == "w" ?'U'+e : 'n'+e}.reverse.join('==\\').unpack(('m'+'x'*4)*11).join}).publish
|
14
|
+
# #Non-asynchroneus distribution and consistend data object for all listeners.
|
15
|
+
# EventAggregator::Message.new("foo3", SomeClass.new(), false, true).publish
|
16
|
+
#
|
2
17
|
class Message
|
3
|
-
attr_accessor :message_type, :data
|
18
|
+
attr_accessor :message_type, :data, :async, :consisten_data
|
4
19
|
@message_type = nil
|
5
20
|
@data = nil
|
6
21
|
@async = nil
|
7
22
|
@consisten_data = nil
|
8
23
|
|
9
|
-
|
24
|
+
|
10
25
|
# Public: Initialize the Message
|
11
|
-
#
|
26
|
+
#
|
12
27
|
# message_type - The type of the message which determine
|
13
|
-
# which
|
14
|
-
#
|
28
|
+
# which EventAggregator::Listener objects will recieve the message
|
29
|
+
# upon publish
|
30
|
+
# data - The data that will be passed to the
|
31
|
+
# EventAggregator::Listener objects
|
15
32
|
# async = true - Indicates if message should be published async or not
|
16
|
-
# consisten_data = false - Indicates if
|
17
|
-
#
|
33
|
+
# consisten_data = false - Indicates if EventAggregator::Listener objects
|
34
|
+
# should recieve a consistent object reference or clones.
|
18
35
|
def initialize(message_type, data, async = true, consisten_data = false)
|
19
36
|
raise "Illegal Message Type" if message_type == nil
|
20
37
|
|
@@ -23,11 +40,12 @@ module EventAggregator
|
|
23
40
|
@async = async
|
24
41
|
@consisten_data = consisten_data
|
25
42
|
end
|
26
|
-
|
27
|
-
# Public: Will publish the message to all
|
28
|
-
#
|
43
|
+
|
44
|
+
# Public: Will publish the message to all instances of
|
45
|
+
# EventAggregator::Listener that is registered for message types
|
46
|
+
# equal to this.message_type
|
29
47
|
def publish
|
30
|
-
Aggregator.message_publish( self
|
48
|
+
Aggregator.message_publish( self )
|
31
49
|
end
|
32
50
|
end
|
33
51
|
end
|
@@ -1,7 +1,17 @@
|
|
1
1
|
module EventAggregator
|
2
|
+
|
3
|
+
# Public: MessageJob is a class used by the EventAggregator::Aggregator
|
4
|
+
# for processing message distribution.
|
5
|
+
#
|
2
6
|
class MessageJob
|
3
7
|
include SuckerPunch::Job
|
4
|
-
|
8
|
+
|
9
|
+
# Public: Duplicate some text an arbitrary number of times.
|
10
|
+
#
|
11
|
+
# data - The data that will be sent to the callback, originating
|
12
|
+
# from a message.
|
13
|
+
# callback - The callback that will be processed with the data as
|
14
|
+
# a parameter
|
5
15
|
def perform(data, callback)
|
6
16
|
callback.call(data)
|
7
17
|
end
|
@@ -210,12 +210,12 @@ describe EventAggregator::Aggregator do
|
|
210
210
|
EventAggregator::Aggregator.register(listener, message_type, callback1)
|
211
211
|
EventAggregator::Aggregator.register(listener2, message_type, callback2)
|
212
212
|
|
213
|
-
message = EventAggregator::Message.new(message_type, data)
|
213
|
+
message = EventAggregator::Message.new(message_type, data, false, true)
|
214
214
|
|
215
215
|
expect(callback1).to receive(:call) {|arg| expect(arg).to equal(data)}
|
216
216
|
expect(callback2).to receive(:call) {|arg| expect(arg).to equal(data)}
|
217
217
|
|
218
|
-
EventAggregator::Aggregator.message_publish(message
|
218
|
+
EventAggregator::Aggregator.message_publish(message)
|
219
219
|
end
|
220
220
|
it 'uses different objects when false' do
|
221
221
|
listener2 = listener_class.new
|
@@ -225,12 +225,12 @@ describe EventAggregator::Aggregator do
|
|
225
225
|
EventAggregator::Aggregator.register(listener, message_type, callback1)
|
226
226
|
EventAggregator::Aggregator.register(listener2, message_type, callback2)
|
227
227
|
|
228
|
-
message = EventAggregator::Message.new(message_type, data)
|
228
|
+
message = EventAggregator::Message.new(message_type, data, false, false)
|
229
229
|
|
230
230
|
expect(callback1).to receive(:call) {|arg| expect(arg).to_not equal(data)}
|
231
231
|
expect(callback2).to receive(:call) {|arg| expect(arg).to_not equal(data)}
|
232
232
|
|
233
|
-
EventAggregator::Aggregator.message_publish(message
|
233
|
+
EventAggregator::Aggregator.message_publish(message)
|
234
234
|
end
|
235
235
|
it 'objects have same values when false' do
|
236
236
|
listener2 = listener_class.new
|
@@ -240,12 +240,12 @@ describe EventAggregator::Aggregator do
|
|
240
240
|
EventAggregator::Aggregator.register(listener, message_type, callback1)
|
241
241
|
EventAggregator::Aggregator.register(listener2, message_type, callback2)
|
242
242
|
|
243
|
-
message = EventAggregator::Message.new(message_type, data)
|
243
|
+
message = EventAggregator::Message.new(message_type, data, false, false)
|
244
244
|
|
245
245
|
expect(callback1).to receive(:call) {|arg| expect(arg).to eq(data)}
|
246
246
|
expect(callback2).to receive(:call) {|arg| expect(arg).to eq(data)}
|
247
247
|
|
248
|
-
EventAggregator::Aggregator.message_publish(message
|
248
|
+
EventAggregator::Aggregator.message_publish(message)
|
249
249
|
end
|
250
250
|
end
|
251
251
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EventAggregator::MessageJob do
|
4
|
+
let(:callback) { lambda{ |data| } }
|
5
|
+
let(:data) { Faker::Name.name }
|
6
|
+
let(:message_job) { EventAggregator::MessageJob.new }
|
7
|
+
|
8
|
+
describe '.perform' do
|
9
|
+
describe 'legal parameters' do
|
10
|
+
it 'excute callback with data' do
|
11
|
+
expect(callback).to receive(:call).with(data)
|
12
|
+
|
13
|
+
message_job.perform(data, callback)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
describe 'illegal parameters' do
|
17
|
+
it 'should never be passed to MessageJob' do
|
18
|
+
expect(true).to eq(true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,10 +2,10 @@ require "rubygems"
|
|
2
2
|
require "bundler/setup"
|
3
3
|
require "factory_girl"
|
4
4
|
require "faker"
|
5
|
-
require "
|
5
|
+
require "event_aggregator"
|
6
6
|
require "sucker_punch/testing/inline"
|
7
7
|
|
8
|
-
|
8
|
+
|
9
9
|
|
10
10
|
RSpec.configure do |config|
|
11
11
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: event_aggregator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.1
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Stephan Eriksen
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-12-
|
12
|
+
date: 2013-12-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- spec/factories.rb
|
132
132
|
- spec/lib/event_aggregator/aggregator_spec.rb
|
133
133
|
- spec/lib/event_aggregator/listener_spec.rb
|
134
|
+
- spec/lib/event_aggregator/message_job_spec.rb
|
134
135
|
- spec/lib/event_aggregator/message_spec.rb
|
135
136
|
- spec/spec_helper.rb
|
136
137
|
homepage: https://github.com/stephan-nordnes-eriksen/event_aggregator
|
@@ -149,9 +150,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
150
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
151
|
none: false
|
151
152
|
requirements:
|
152
|
-
- - ! '
|
153
|
+
- - ! '>='
|
153
154
|
- !ruby/object:Gem::Version
|
154
|
-
version:
|
155
|
+
version: '0'
|
155
156
|
requirements: []
|
156
157
|
rubyforge_project:
|
157
158
|
rubygems_version: 1.8.25
|
@@ -162,5 +163,6 @@ test_files:
|
|
162
163
|
- spec/factories.rb
|
163
164
|
- spec/lib/event_aggregator/aggregator_spec.rb
|
164
165
|
- spec/lib/event_aggregator/listener_spec.rb
|
166
|
+
- spec/lib/event_aggregator/message_job_spec.rb
|
165
167
|
- spec/lib/event_aggregator/message_spec.rb
|
166
168
|
- spec/spec_helper.rb
|