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