empipelines 0.2.1 → 0.2.2
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/README.md +0 -1
- data/Rakefile +1 -0
- data/empipelines.gemspec +6 -2
- data/functional/consuming_events_from_file_spec.rb +4 -16
- data/functional/consuming_events_from_multiple_sources_spec.rb +31 -0
- data/functional/consuming_events_from_queue_spec.rb +32 -0
- data/functional/events.dat +3 -10
- data/functional/test_stages.rb +1 -1
- data/lib/empipelines/aggregated_event_source.rb +24 -0
- data/lib/empipelines/amqp_event_source.rb +1 -0
- data/lib/empipelines/batch_event_source.rb +7 -3
- data/lib/empipelines/io_event_source.rb +1 -1
- data/lib/empipelines/message.rb +14 -9
- data/lib/empipelines/periodic_event_source.rb +1 -0
- data/lib/empipelines/pipeline.rb +2 -2
- data/lib/empipelines.rb +6 -3
- data/unit/empipelines/aggregated_event_source_spec.rb +85 -0
- data/unit/empipelines/batch_event_source_spec.rb +15 -16
- data/unit/empipelines/io_event_source_spec.rb +9 -7
- data/unit/empipelines/message_spec.rb +3 -5
- data/unit/empipelines/pipeline_spec.rb +61 -17
- metadata +7 -3
data/README.md
CHANGED
data/Rakefile
CHANGED
data/empipelines.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.rubygems_version = '1.3.5'
|
5
5
|
## Leave these as is they will be modified for you by the rake gemspec task.
|
6
6
|
s.name = 'empipelines'
|
7
|
-
s.version = '0.2.
|
8
|
-
s.date = '2011-12-
|
7
|
+
s.version = '0.2.2'
|
8
|
+
s.date = '2011-12-23'
|
9
9
|
s.rubyforge_project = 'empipelines'
|
10
10
|
|
11
11
|
s.summary = "Simple Event Handling Pipeline Architecture for EventMachine"
|
@@ -37,9 +37,12 @@ Gem::Specification.new do |s|
|
|
37
37
|
Rakefile
|
38
38
|
empipelines.gemspec
|
39
39
|
functional/consuming_events_from_file_spec.rb
|
40
|
+
functional/consuming_events_from_multiple_sources_spec.rb
|
41
|
+
functional/consuming_events_from_queue_spec.rb
|
40
42
|
functional/events.dat
|
41
43
|
functional/test_stages.rb
|
42
44
|
lib/empipelines.rb
|
45
|
+
lib/empipelines/aggregated_event_source.rb
|
43
46
|
lib/empipelines/amqp_event_source.rb
|
44
47
|
lib/empipelines/batch_event_source.rb
|
45
48
|
lib/empipelines/event_handlers.rb
|
@@ -49,6 +52,7 @@ Gem::Specification.new do |s|
|
|
49
52
|
lib/empipelines/message.rb
|
50
53
|
lib/empipelines/periodic_event_source.rb
|
51
54
|
lib/empipelines/pipeline.rb
|
55
|
+
unit/empipelines/aggregated_event_source_spec.rb
|
52
56
|
unit/empipelines/amqp_event_source_spec.rb
|
53
57
|
unit/empipelines/batch_event_source_spec.rb
|
54
58
|
unit/empipelines/empty_io_event_source.dat
|
@@ -10,29 +10,17 @@ module TestStages
|
|
10
10
|
include EmRunner
|
11
11
|
|
12
12
|
it 'consumes all events from the file' do
|
13
|
-
with_em_run do
|
13
|
+
with_em_run do
|
14
14
|
pipeline = EmPipelines::Pipeline.new(EM, {:processed => processed}, monitoring, logger)
|
15
15
|
|
16
16
|
file_name = File.join(File.dirname(__FILE__), 'events.dat')
|
17
17
|
source = EmPipelines::IOEventSource.new(EM, file_name)
|
18
18
|
|
19
|
-
stages = [PassthroughStage, PassthroughStage, PassthroughStage
|
19
|
+
stages = [PassthroughStage, PassthroughStage, PassthroughStage]
|
20
20
|
event_pipeline = EmPipelines::EventPipeline.new(source, pipeline.for(stages), monitoring)
|
21
21
|
|
22
|
-
source.on_finished do |
|
23
|
-
EM.stop
|
24
|
-
messages.should have(10).items
|
25
|
-
messages[0][:payload].should ==("event #0")
|
26
|
-
messages[1][:payload].should ==("event #1")
|
27
|
-
messages[2][:payload].should ==("event #2")
|
28
|
-
messages[3][:payload].should ==("event #3")
|
29
|
-
messages[4][:payload].should ==("event #4")
|
30
|
-
messages[5][:payload].should ==("event #5")
|
31
|
-
messages[6][:payload].should ==("event #6")
|
32
|
-
messages[7][:payload].should ==("event #7")
|
33
|
-
messages[8][:payload].should ==("event #8")
|
34
|
-
messages[9][:payload].should ==("event #9")
|
35
|
-
messages.each { |m| m.state.should ==(:consumed) }
|
22
|
+
source.on_finished do |s|
|
23
|
+
EM.stop
|
36
24
|
end
|
37
25
|
|
38
26
|
event_pipeline.start!
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'empipelines'
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_stages')
|
4
|
+
|
5
|
+
module TestStages
|
6
|
+
describe 'Consumption of events from multiple sources' do
|
7
|
+
let(:monitoring) { stub() }
|
8
|
+
let(:logger) { stub(:info => nil, :debug => nil) }
|
9
|
+
let (:processed) { {} }
|
10
|
+
include EmRunner
|
11
|
+
|
12
|
+
it 'consumes all events from all sources' do
|
13
|
+
pipeline = EmPipelines::Pipeline.new(EM, {:processed => processed}, monitoring, logger)
|
14
|
+
|
15
|
+
file_name = File.join(File.dirname(__FILE__), 'events.dat')
|
16
|
+
num_events_on_file = IO.readlines(file_name).size
|
17
|
+
io_source = EmPipelines::IOEventSource.new(EM, file_name)
|
18
|
+
|
19
|
+
batch = (1...1000).to_a
|
20
|
+
batch_name = "my batch!"
|
21
|
+
batch_source = EmPipelines::BatchEventSource.new(EM, batch_name, batch)
|
22
|
+
|
23
|
+
composed_event_source = EmPipelines::AggregatedEventSource.new(EM, batch_source, io_source)
|
24
|
+
|
25
|
+
composed_event_source.on_finished do |s|
|
26
|
+
EM.stop
|
27
|
+
s.should ==(composed_event_source)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'empipelines'
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_stages')
|
4
|
+
|
5
|
+
module TestStages
|
6
|
+
describe 'Consumption of events from a queue' do
|
7
|
+
let(:monitoring) { stub() }
|
8
|
+
let(:logger) { stub(:info => nil, :debug => nil) }
|
9
|
+
let (:processed) { {} }
|
10
|
+
include EmRunner
|
11
|
+
|
12
|
+
it 'consumes all events from the a queue' do
|
13
|
+
with_em_run do
|
14
|
+
pipeline = EmPipelines::Pipeline.new(EM, {:processed => processed}, monitoring, logger)
|
15
|
+
|
16
|
+
batch = (1...1000).to_a
|
17
|
+
batch_name = "my batch!"
|
18
|
+
source = EmPipelines::BatchEventSource.new(EM, batch_name, batch)
|
19
|
+
|
20
|
+
stages = [PassthroughStage, PassthroughStage, PassthroughStage]
|
21
|
+
event_pipeline = EmPipelines::EventPipeline.new(source, pipeline.for(stages), monitoring)
|
22
|
+
|
23
|
+
source.on_finished do |s|
|
24
|
+
EM.stop
|
25
|
+
s.should ==(source)
|
26
|
+
end
|
27
|
+
|
28
|
+
event_pipeline.start!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/functional/events.dat
CHANGED
@@ -1,10 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
event #3
|
5
|
-
event #4
|
6
|
-
event #5
|
7
|
-
event #6
|
8
|
-
event #7
|
9
|
-
event #8
|
10
|
-
event #9
|
1
|
+
1baa575127c661dfa754a43238b51bdb6adbc0ead0a3b1abec0d45e394ee19f1
|
2
|
+
c9730aa998cda290da19a311d19bb91a6aefa3552795dfbd8a2083bf833ba645
|
3
|
+
3fc87b82e170d9e01889537f1dee465e829461178b6eb0ea49b0429e4c75d798
|
data/functional/test_stages.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'empipelines/event_handlers'
|
2
|
+
|
3
|
+
module EmPipelines
|
4
|
+
class AggregatedEventSource
|
5
|
+
include EventHandlers
|
6
|
+
|
7
|
+
def initialize(em, *event_sources)
|
8
|
+
@em, @sources = em, event_sources.flatten
|
9
|
+
end
|
10
|
+
|
11
|
+
def start!
|
12
|
+
finished = 0
|
13
|
+
@sources.each do |s|
|
14
|
+
s.on_event(event_handler)
|
15
|
+
|
16
|
+
s.on_finished do |*ignored|
|
17
|
+
finished += 1
|
18
|
+
finished_handler.call(self) if finished == @sources.size
|
19
|
+
end
|
20
|
+
@em.next_tick { s.start! }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -5,6 +5,7 @@ module EmPipelines
|
|
5
5
|
include EventHandlers
|
6
6
|
|
7
7
|
def initialize(em, list_name, events)
|
8
|
+
@num_finalised = 0
|
8
9
|
@em, @list_name, @events = em, list_name,events
|
9
10
|
end
|
10
11
|
|
@@ -13,7 +14,7 @@ module EmPipelines
|
|
13
14
|
check_if_finished
|
14
15
|
|
15
16
|
message_finished = lambda do |m|
|
16
|
-
@
|
17
|
+
@num_finalised += 1
|
17
18
|
check_if_finished
|
18
19
|
end
|
19
20
|
|
@@ -33,10 +34,13 @@ module EmPipelines
|
|
33
34
|
|
34
35
|
private
|
35
36
|
def check_if_finished
|
36
|
-
|
37
|
+
#TODO: can we make this not be based on size?
|
38
|
+
#it makes it harder to have streams as event sources (i.e. ranges).
|
39
|
+
#this class should only rely on Enumerable methods.
|
40
|
+
finished = (@num_finalised == @events.size)
|
37
41
|
|
38
42
|
if finished and finished_handler
|
39
|
-
@em.next_tick { finished_handler.call(
|
43
|
+
@em.next_tick { finished_handler.call(self) }
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
@@ -15,7 +15,7 @@ module EmPipelines
|
|
15
15
|
|
16
16
|
wrapped_handler = BatchEventSource.new(@em, @file_path, events)
|
17
17
|
wrapped_handler.on_event(event_handler)
|
18
|
-
wrapped_handler.on_finished(
|
18
|
+
wrapped_handler.on_finished { |*ignored| finished_handler.call(self) }
|
19
19
|
wrapped_handler.start!
|
20
20
|
end
|
21
21
|
end
|
data/lib/empipelines/message.rb
CHANGED
@@ -3,27 +3,28 @@ module EmPipelines
|
|
3
3
|
attr_reader :state
|
4
4
|
|
5
5
|
def initialize(base_hash={})
|
6
|
-
|
6
|
+
backing_hash!(base_hash)
|
7
7
|
created!
|
8
8
|
end
|
9
9
|
|
10
10
|
def [](key)
|
11
|
-
|
11
|
+
as_hash[key]
|
12
12
|
end
|
13
13
|
|
14
14
|
def []=(key, value)
|
15
15
|
check_if_mutation_allowed
|
16
|
-
|
16
|
+
as_hash[key] = value
|
17
17
|
end
|
18
18
|
|
19
19
|
def delete(key)
|
20
20
|
check_if_mutation_allowed
|
21
|
-
|
21
|
+
as_hash.delete key
|
22
22
|
end
|
23
23
|
|
24
|
-
def merge(other_hash)
|
24
|
+
def merge!(other_hash)
|
25
25
|
check_if_mutation_allowed
|
26
|
-
|
26
|
+
backing_hash!(as_hash.merge(other_hash))
|
27
|
+
self
|
27
28
|
end
|
28
29
|
|
29
30
|
def on_consumed(callback=nil, &callback_block)
|
@@ -56,17 +57,21 @@ module EmPipelines
|
|
56
57
|
invoke(@rejected_broken_callback)
|
57
58
|
end
|
58
59
|
|
59
|
-
def
|
60
|
+
def as_hash
|
60
61
|
@backing_hash
|
61
62
|
end
|
63
|
+
|
64
|
+
def payload
|
65
|
+
as_hash[:payload]
|
66
|
+
end
|
62
67
|
|
63
68
|
def to_s
|
64
|
-
"#{self.class.name} state:#{@state}
|
69
|
+
"#{self.class.name} state:#{@state} backing_hash:#{as_hash}"
|
65
70
|
end
|
66
71
|
|
67
72
|
private
|
68
73
|
|
69
|
-
def
|
74
|
+
def backing_hash!(other)
|
70
75
|
@backing_hash = symbolised(other)
|
71
76
|
end
|
72
77
|
|
data/lib/empipelines/pipeline.rb
CHANGED
data/lib/empipelines.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
module EmPipelines
|
2
|
-
VERSION = '0.2.
|
2
|
+
VERSION = '0.2.2'
|
3
3
|
end
|
4
4
|
|
5
|
+
require 'empipelines/message'
|
6
|
+
|
5
7
|
require 'empipelines/event_handlers'
|
6
8
|
require 'empipelines/amqp_event_source'
|
7
9
|
require 'empipelines/batch_event_source'
|
8
10
|
require 'empipelines/io_event_source'
|
9
|
-
require 'empipelines/event_pipeline'
|
10
11
|
require 'empipelines/list_event_source'
|
11
|
-
require 'empipelines/message'
|
12
12
|
require 'empipelines/periodic_event_source'
|
13
|
+
require 'empipelines/aggregated_event_source'
|
14
|
+
|
15
|
+
require 'empipelines/event_pipeline'
|
13
16
|
require 'empipelines/pipeline'
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'empipelines/aggregated_event_source'
|
2
|
+
|
3
|
+
module EmPipelines
|
4
|
+
class EventSourceStub
|
5
|
+
include EventHandlers
|
6
|
+
|
7
|
+
def event!(contents)
|
8
|
+
raise 'not started' unless @started
|
9
|
+
event_handler.call(contents) if event_handler
|
10
|
+
end
|
11
|
+
|
12
|
+
def finish!
|
13
|
+
raise 'not started' unless @started
|
14
|
+
finished_handler.call([:this, :should, :not, :be, :used])
|
15
|
+
end
|
16
|
+
|
17
|
+
def start!
|
18
|
+
@started = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe AggregatedEventSource do
|
23
|
+
|
24
|
+
let (:em) do
|
25
|
+
em = mock('eventmachine')
|
26
|
+
em.stub(:next_tick).and_yield
|
27
|
+
em
|
28
|
+
end
|
29
|
+
|
30
|
+
let (:list_name) { "list of stuff" }
|
31
|
+
|
32
|
+
it 'sends each sends messages from all sources, as they happen, to listeners' do
|
33
|
+
source1, source2, source3 = EventSourceStub.new, EventSourceStub.new, EventSourceStub.new
|
34
|
+
|
35
|
+
aggregated = AggregatedEventSource.new(em, source1, source2, source3)
|
36
|
+
|
37
|
+
expected = (0..4).map{ |i| stub("Message #{i}") }
|
38
|
+
received = []
|
39
|
+
aggregated.on_event { |m| received << m}
|
40
|
+
|
41
|
+
aggregated.start!
|
42
|
+
|
43
|
+
source1.event! expected[0]
|
44
|
+
source2.event! expected[1]
|
45
|
+
source2.event! expected[2]
|
46
|
+
source3.event! expected[3]
|
47
|
+
source1.event! expected[4]
|
48
|
+
|
49
|
+
received.should ==(expected)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'calls the finished handler when all sources finished' do
|
53
|
+
sources = [EventSourceStub.new, EventSourceStub.new, EventSourceStub.new]
|
54
|
+
|
55
|
+
aggregated = AggregatedEventSource.new(em, sources)
|
56
|
+
|
57
|
+
has_finished = [false]
|
58
|
+
aggregated.on_finished do |s|
|
59
|
+
s.should ==(aggregated)
|
60
|
+
has_finished[0] = true
|
61
|
+
end
|
62
|
+
|
63
|
+
aggregated.start!
|
64
|
+
sources[2].finish!
|
65
|
+
sources[1].finish!
|
66
|
+
sources[0].finish!
|
67
|
+
|
68
|
+
has_finished.first.should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'does not call the finished handler if a source is still going' do
|
72
|
+
sources = [EventSourceStub.new, EventSourceStub.new, EventSourceStub.new]
|
73
|
+
|
74
|
+
aggregated = AggregatedEventSource.new(em, sources)
|
75
|
+
|
76
|
+
aggregated.on_finished do |s|
|
77
|
+
raise 'should never happen'
|
78
|
+
end
|
79
|
+
|
80
|
+
aggregated.start!
|
81
|
+
sources[2].finish!
|
82
|
+
sources[0].finish!
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'empipelines/batch_event_source'
|
2
2
|
|
3
3
|
module EmPipelines
|
4
|
+
ShouldNotBeCalled = lambda { raise 'should not be called' }
|
4
5
|
describe BatchEventSource do
|
5
6
|
|
6
7
|
let (:em) do
|
@@ -9,9 +10,9 @@ module EmPipelines
|
|
9
10
|
em
|
10
11
|
end
|
11
12
|
|
12
|
-
let (:list_name) {
|
13
|
+
let (:list_name) { 'list of stuff' }
|
13
14
|
|
14
|
-
it
|
15
|
+
it 'sends each element on the list as a payload to the listener' do
|
15
16
|
events = [1,2,3,4,5,6,7,8,9,10]
|
16
17
|
source = BatchEventSource.new(em, list_name, events)
|
17
18
|
|
@@ -26,14 +27,15 @@ module EmPipelines
|
|
26
27
|
received.each{ |i| i[:origin].should == list_name }
|
27
28
|
end
|
28
29
|
|
29
|
-
it
|
30
|
+
it 'calls the batch finished callback when all items were processed' do
|
30
31
|
events = [1,2,3,4,5,6,7,8,9,10]
|
31
32
|
source = BatchEventSource.new(em, list_name, events)
|
32
33
|
|
33
|
-
has_finished = []
|
34
|
+
has_finished = [false]
|
34
35
|
|
35
|
-
source.on_finished do |
|
36
|
-
|
36
|
+
source.on_finished do |s|
|
37
|
+
s.should ==(source)
|
38
|
+
has_finished[0] = true
|
37
39
|
end
|
38
40
|
|
39
41
|
source.on_event do |e|
|
@@ -42,33 +44,30 @@ module EmPipelines
|
|
42
44
|
|
43
45
|
source.start!
|
44
46
|
|
45
|
-
has_finished.first.
|
47
|
+
has_finished.first.should be_true
|
46
48
|
end
|
47
49
|
|
48
|
-
it
|
50
|
+
it 'finishes immediately if there are no events to process' do
|
49
51
|
source = BatchEventSource.new(em, list_name, [])
|
50
52
|
|
51
53
|
has_finished = []
|
52
|
-
source.on_finished do |
|
54
|
+
source.on_finished do |s|
|
55
|
+
s.should ==(source)
|
53
56
|
has_finished << true
|
54
57
|
end
|
55
58
|
|
56
|
-
source.on_event
|
57
|
-
raise 'should not be called!'
|
58
|
-
end
|
59
|
+
source.on_event(&ShouldNotBeCalled)
|
59
60
|
|
60
61
|
source.start!
|
61
62
|
|
62
63
|
has_finished.first.should be_true
|
63
64
|
end
|
64
65
|
|
65
|
-
it
|
66
|
+
it 'only calls the finished handler if all events were processed' do
|
66
67
|
events = [1,2,3,4,5,6,7,8,9,10]
|
67
68
|
source = BatchEventSource.new(em, list_name, events)
|
68
69
|
|
69
|
-
source.
|
70
|
-
raise "should not be called"
|
71
|
-
end
|
70
|
+
source.on_event(&ShouldNotBeCalled)
|
72
71
|
|
73
72
|
count = 0
|
74
73
|
source.on_event do |e|
|
@@ -33,10 +33,11 @@ module EmPipelines
|
|
33
33
|
it 'calls the finished callback when all messages were processed' do
|
34
34
|
source = IOEventSource.new(em, events_file)
|
35
35
|
|
36
|
-
has_finished = []
|
36
|
+
has_finished = [false]
|
37
37
|
|
38
|
-
source.on_finished do |
|
39
|
-
|
38
|
+
source.on_finished do |s|
|
39
|
+
s.should ==(source)
|
40
|
+
has_finished[0] = true
|
40
41
|
end
|
41
42
|
|
42
43
|
source.on_event do |e|
|
@@ -45,15 +46,16 @@ module EmPipelines
|
|
45
46
|
|
46
47
|
source.start!
|
47
48
|
|
48
|
-
has_finished.first.
|
49
|
+
has_finished.first.should be_true
|
49
50
|
end
|
50
51
|
|
51
52
|
it 'finishes immediately if there are no events to process' do
|
52
53
|
source = IOEventSource.new(em, empty_file)
|
53
54
|
|
54
|
-
has_finished = []
|
55
|
-
source.on_finished do |
|
56
|
-
|
55
|
+
has_finished = [false]
|
56
|
+
source.on_finished do |s|
|
57
|
+
s.should ==(source)
|
58
|
+
has_finished[0] = true
|
57
59
|
end
|
58
60
|
|
59
61
|
source.on_event do |e|
|
@@ -46,12 +46,10 @@ module EmPipelines
|
|
46
46
|
|
47
47
|
it "can be merged with a map, symbolising keys" do
|
48
48
|
original = Message.new({'a' => 1})
|
49
|
-
|
49
|
+
original.merge!({'b' => 2})
|
50
50
|
|
51
|
-
original[:
|
52
|
-
|
53
|
-
merged[:a].should ==(original[:a])
|
54
|
-
merged[:b].should_not be_nil
|
51
|
+
original[:a].should ==(1)
|
52
|
+
original[:b].should ==(2)
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
@@ -1,21 +1,45 @@
|
|
1
|
-
require
|
1
|
+
require 'empipelines/pipeline'
|
2
|
+
|
3
|
+
def msg(some_map)
|
4
|
+
EmPipelines:: MessageMock.new(some_map)
|
5
|
+
end
|
2
6
|
|
3
7
|
module EmPipelines
|
8
|
+
class MessageMock < EmPipelines::Message
|
9
|
+
def consumed!
|
10
|
+
raise 'unexpected call'
|
11
|
+
end
|
12
|
+
|
13
|
+
def rejected!
|
14
|
+
raise 'unexpected call'
|
15
|
+
end
|
16
|
+
|
17
|
+
def broken!
|
18
|
+
raise 'unexpected call'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
4
22
|
class AddOne
|
5
23
|
def call(input, &next_stage)
|
6
|
-
next_stage.call({:data => (input[:data] + 1)})
|
24
|
+
next_stage.call(input.merge!({:data => (input[:data] + 1)}))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Passthrough
|
29
|
+
def call(input, &next_stage)
|
30
|
+
next_stage.call(input)
|
7
31
|
end
|
8
32
|
end
|
9
33
|
|
10
34
|
class SquareIt
|
11
35
|
def call(input, &next_stage)
|
12
|
-
next_stage.call({:data => (input[:data] * input[:data])})
|
36
|
+
next_stage.call(input.merge!({:data => (input[:data] * input[:data])}))
|
13
37
|
end
|
14
38
|
end
|
15
39
|
|
16
40
|
class BrokenStage
|
17
41
|
def call(ignore, &ignored_too)
|
18
|
-
raise
|
42
|
+
raise 'Boo!'
|
19
43
|
end
|
20
44
|
end
|
21
45
|
|
@@ -27,13 +51,13 @@ module EmPipelines
|
|
27
51
|
|
28
52
|
class NeedsAnApple
|
29
53
|
def call(input, &next_stage)
|
30
|
-
next_stage.call(input.merge({:apple => apple}))
|
54
|
+
next_stage.call(input.merge!({:apple => apple}))
|
31
55
|
end
|
32
56
|
end
|
33
57
|
|
34
58
|
class NeedsAnOrange
|
35
59
|
def call(input, &next_stage)
|
36
|
-
next_stage.call(input.merge({:orange => orange}))
|
60
|
+
next_stage.call(input.merge!({:orange => orange}))
|
37
61
|
end
|
38
62
|
end
|
39
63
|
|
@@ -49,7 +73,7 @@ module EmPipelines
|
|
49
73
|
|
50
74
|
def call(input, &next_step)
|
51
75
|
@@value = input
|
52
|
-
next_step.call(
|
76
|
+
next_step.call(input)
|
53
77
|
end
|
54
78
|
end
|
55
79
|
|
@@ -72,35 +96,55 @@ module EmPipelines
|
|
72
96
|
|
73
97
|
describe Pipeline do
|
74
98
|
let(:logger) {stub(:info => true, :debug => true)}
|
75
|
-
|
99
|
+
|
100
|
+
it 'chains the actions using processes' do
|
76
101
|
event_chain = [AddOne, SquareIt, GlobalHolder]
|
102
|
+
a_msg = msg({:data =>1})
|
103
|
+
a_msg.should_receive(:consumed!)
|
104
|
+
|
77
105
|
pipelines = Pipeline.new(StubSpawner.new, {}, stub('monitoring'), logger)
|
78
106
|
pipeline = pipelines.for(event_chain)
|
79
|
-
pipeline.notify(
|
80
|
-
|
107
|
+
pipeline.notify(a_msg)
|
108
|
+
|
109
|
+
GlobalHolder.held[:data].should ==(4)
|
81
110
|
end
|
82
111
|
|
83
|
-
it
|
112
|
+
it 'does not send to the next if last returned nil' do
|
84
113
|
event_chain = [AddOne, SquareIt, DeadEnd, GlobalHolder]
|
85
114
|
pipelines = Pipeline.new(StubSpawner.new, {}, stub('monitoring'), logger)
|
86
115
|
pipeline = pipelines.for(event_chain)
|
87
|
-
pipeline.notify({:data => 1})
|
116
|
+
pipeline.notify(msg({:data => 1}))
|
88
117
|
GlobalHolder.held.should be_nil
|
89
118
|
end
|
90
119
|
|
91
|
-
it
|
120
|
+
it 'makes all objects in the context object available to stages' do
|
92
121
|
event_chain = [NeedsAnApple, NeedsAnOrange, GlobalHolder]
|
93
122
|
pipelines = Pipeline.new(StubSpawner.new, {:apple => :some_apple, :orange => :some_orange}, stub('monitoring'), logger)
|
123
|
+
a_msg = msg({})
|
124
|
+
a_msg.should_receive(:consumed!)
|
125
|
+
|
94
126
|
pipeline = pipelines.for(event_chain)
|
95
|
-
pipeline.notify(
|
96
|
-
|
127
|
+
pipeline.notify(a_msg)
|
128
|
+
|
129
|
+
GlobalHolder.held[:apple].should ==(:some_apple)
|
130
|
+
GlobalHolder.held[:orange].should ==(:some_orange)
|
97
131
|
end
|
98
132
|
|
99
|
-
it
|
133
|
+
it 'sends exception to the proper handler' do
|
100
134
|
monitoring = mock()
|
101
135
|
monitoring.should_receive(:inform_exception!)
|
102
136
|
pipeline = Pipeline.new(StubSpawner.new, {}, monitoring, logger)
|
103
|
-
pipeline.for([BrokenStage]).notify({})
|
137
|
+
pipeline.for([BrokenStage]).notify(msg({}))
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'flags the message as consumed if goest through all stages' do
|
141
|
+
event_chain = [Passthrough, Passthrough]
|
142
|
+
pipelines = Pipeline.new(StubSpawner.new, {}, stub('monitoring'), logger)
|
143
|
+
pipeline = pipelines.for(event_chain)
|
144
|
+
a_msg = msg({:data => :whatevah})
|
145
|
+
a_msg.should_receive(:consumed!)
|
146
|
+
|
147
|
+
pipeline.notify(a_msg)
|
104
148
|
end
|
105
149
|
end
|
106
150
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: empipelines
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-12-
|
13
|
+
date: 2011-12-23 00:00:00.000000000Z
|
14
14
|
dependencies: []
|
15
15
|
description: Simple Event Handling Pipeline Architecture for EventMachine
|
16
16
|
email: pcalcado+empipelines@gmail.com
|
@@ -25,9 +25,12 @@ files:
|
|
25
25
|
- Rakefile
|
26
26
|
- empipelines.gemspec
|
27
27
|
- functional/consuming_events_from_file_spec.rb
|
28
|
+
- functional/consuming_events_from_multiple_sources_spec.rb
|
29
|
+
- functional/consuming_events_from_queue_spec.rb
|
28
30
|
- functional/events.dat
|
29
31
|
- functional/test_stages.rb
|
30
32
|
- lib/empipelines.rb
|
33
|
+
- lib/empipelines/aggregated_event_source.rb
|
31
34
|
- lib/empipelines/amqp_event_source.rb
|
32
35
|
- lib/empipelines/batch_event_source.rb
|
33
36
|
- lib/empipelines/event_handlers.rb
|
@@ -37,6 +40,7 @@ files:
|
|
37
40
|
- lib/empipelines/message.rb
|
38
41
|
- lib/empipelines/periodic_event_source.rb
|
39
42
|
- lib/empipelines/pipeline.rb
|
43
|
+
- unit/empipelines/aggregated_event_source_spec.rb
|
40
44
|
- unit/empipelines/amqp_event_source_spec.rb
|
41
45
|
- unit/empipelines/batch_event_source_spec.rb
|
42
46
|
- unit/empipelines/empty_io_event_source.dat
|
@@ -64,7 +68,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
68
|
version: '0'
|
65
69
|
segments:
|
66
70
|
- 0
|
67
|
-
hash: -
|
71
|
+
hash: -530546752871247334
|
68
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
73
|
none: false
|
70
74
|
requirements:
|