empipelines 0.2 → 0.2.1
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 +13 -0
- data/Rakefile +15 -14
- data/empipelines.gemspec +19 -11
- data/functional/consuming_events_from_file_spec.rb +42 -0
- data/functional/events.dat +10 -0
- data/functional/test_stages.rb +67 -0
- data/lib/empipelines/amqp_event_source.rb +7 -6
- data/lib/empipelines/batch_event_source.rb +13 -14
- data/lib/empipelines/event_handlers.rb +20 -0
- data/lib/empipelines/io_event_source.rb +22 -0
- data/lib/empipelines/message.rb +2 -0
- data/lib/empipelines/periodic_event_source.rb +7 -3
- data/lib/empipelines/pipeline.rb +3 -3
- data/lib/empipelines.rb +3 -1
- data/{spec → unit}/empipelines/amqp_event_source_spec.rb +7 -2
- data/{spec → unit}/empipelines/batch_event_source_spec.rb +12 -9
- data/{spec/spec_helper.rb → unit/empipelines/empty_io_event_source.dat} +0 -0
- data/{spec → unit}/empipelines/event_pipeline_spec.rb +0 -0
- data/unit/empipelines/io_event_source.dat +3 -0
- data/unit/empipelines/io_event_source_spec.rb +87 -0
- data/{spec → unit}/empipelines/list_event_source_spec.rb +0 -0
- data/{spec → unit}/empipelines/message_spec.rb +0 -0
- data/unit/empipelines/periodic_event_source_spec.rb +31 -0
- data/{spec → unit}/empipelines/pipeline_spec.rb +0 -0
- data/unit/spec_helper.rb +0 -0
- data/{spec → unit}/stage_helper.rb +0 -0
- metadata +20 -12
- data/spec/empipelines/periodic_event_source_spec.rb +0 -28
data/README.md
CHANGED
@@ -1 +1,14 @@
|
|
1
1
|
# EM Pipelines
|
2
|
+
|
3
|
+
## TODO
|
4
|
+
* Make wiring easier
|
5
|
+
* Example apps
|
6
|
+
* TerminatorStage should consume message
|
7
|
+
* Control flow for AmqpEventSource
|
8
|
+
* Transaction ID on each message
|
9
|
+
* Evented I/O for IOEventSource
|
10
|
+
* Consolidate logger and monitoring
|
11
|
+
* Default monitoring implementation
|
12
|
+
* Performance testing
|
13
|
+
* Make all events composable (e.g. new_on_event = old_on_event o new_handler)
|
14
|
+
* Detect insonsistency when handler didnt consume or pass message ahead
|
data/Rakefile
CHANGED
@@ -59,12 +59,23 @@ Cucumber::Rake::Task.new(:all_features) do |t|
|
|
59
59
|
t.cucumber_opts = "--format pretty"
|
60
60
|
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
all = FileList[
|
62
|
+
def spec(dir)
|
63
|
+
puts "---------- #{dir} tests... ----------"
|
64
|
+
all = FileList["#{dir}/**/*_spec.rb"]
|
65
65
|
sh "rspec --color #{all}"
|
66
66
|
end
|
67
67
|
|
68
|
+
desc "Runs unit tests"
|
69
|
+
task :unit do
|
70
|
+
spec('unit')
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "Run functional tests"
|
74
|
+
task :functional do
|
75
|
+
spec('functional')
|
76
|
+
end
|
77
|
+
|
78
|
+
|
68
79
|
desc "Resets localhost's rabbitmq"
|
69
80
|
task :reset_rabbitmq do
|
70
81
|
# sh 'rabbitmqctl stop_app; echo 0'
|
@@ -72,17 +83,7 @@ task :reset_rabbitmq do
|
|
72
83
|
# sh 'rabbitmqctl start_app'
|
73
84
|
end
|
74
85
|
|
75
|
-
|
76
|
-
task :features do
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
desc "Run cucumber tests for all features, including work in progress"
|
81
|
-
task :all_features do
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
task :ci => [:specs, :features, :build]
|
86
|
+
task :ci => [:unit, :functional, :build]
|
86
87
|
|
87
88
|
desc "MUST BE RUN (AND PASS!) BEFORE CHECKING IN CODE!"
|
88
89
|
task :pre_checkin => [:reset_rabbitmq, :ci]
|
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.1'
|
8
|
+
s.date = '2011-12-21'
|
9
9
|
s.rubyforge_project = 'empipelines'
|
10
10
|
|
11
11
|
s.summary = "Simple Event Handling Pipeline Architecture for EventMachine"
|
@@ -36,23 +36,31 @@ Gem::Specification.new do |s|
|
|
36
36
|
README.md
|
37
37
|
Rakefile
|
38
38
|
empipelines.gemspec
|
39
|
+
functional/consuming_events_from_file_spec.rb
|
40
|
+
functional/events.dat
|
41
|
+
functional/test_stages.rb
|
39
42
|
lib/empipelines.rb
|
40
43
|
lib/empipelines/amqp_event_source.rb
|
41
44
|
lib/empipelines/batch_event_source.rb
|
45
|
+
lib/empipelines/event_handlers.rb
|
42
46
|
lib/empipelines/event_pipeline.rb
|
47
|
+
lib/empipelines/io_event_source.rb
|
43
48
|
lib/empipelines/list_event_source.rb
|
44
49
|
lib/empipelines/message.rb
|
45
50
|
lib/empipelines/periodic_event_source.rb
|
46
51
|
lib/empipelines/pipeline.rb
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
unit/empipelines/amqp_event_source_spec.rb
|
53
|
+
unit/empipelines/batch_event_source_spec.rb
|
54
|
+
unit/empipelines/empty_io_event_source.dat
|
55
|
+
unit/empipelines/event_pipeline_spec.rb
|
56
|
+
unit/empipelines/io_event_source.dat
|
57
|
+
unit/empipelines/io_event_source_spec.rb
|
58
|
+
unit/empipelines/list_event_source_spec.rb
|
59
|
+
unit/empipelines/message_spec.rb
|
60
|
+
unit/empipelines/periodic_event_source_spec.rb
|
61
|
+
unit/empipelines/pipeline_spec.rb
|
62
|
+
unit/spec_helper.rb
|
63
|
+
unit/stage_helper.rb
|
56
64
|
]
|
57
65
|
# = MANIFEST =
|
58
66
|
|
@@ -0,0 +1,42 @@
|
|
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 file' 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 file' do
|
13
|
+
with_em_run do
|
14
|
+
pipeline = EmPipelines::Pipeline.new(EM, {:processed => processed}, monitoring, logger)
|
15
|
+
|
16
|
+
file_name = File.join(File.dirname(__FILE__), 'events.dat')
|
17
|
+
source = EmPipelines::IOEventSource.new(EM, file_name)
|
18
|
+
|
19
|
+
stages = [PassthroughStage, PassthroughStage, PassthroughStage, ConsumeStage]
|
20
|
+
event_pipeline = EmPipelines::EventPipeline.new(source, pipeline.for(stages), monitoring)
|
21
|
+
|
22
|
+
source.on_finished do |messages|
|
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) }
|
36
|
+
end
|
37
|
+
|
38
|
+
event_pipeline.start!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module TestStages
|
2
|
+
module EmRunner
|
3
|
+
def with_em_run(&test_body)
|
4
|
+
EM.run do
|
5
|
+
EM.add_timer(10) do
|
6
|
+
EM.stop
|
7
|
+
raise "################### Test timed-out! ################### "
|
8
|
+
end
|
9
|
+
|
10
|
+
test_body.call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module SomeStage
|
16
|
+
@@last_id = 0
|
17
|
+
def initialize
|
18
|
+
@id = "module ##{(@@last_id)}"
|
19
|
+
@@last_id += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(message, &callback)
|
23
|
+
processed[self.class] = message
|
24
|
+
process(message, callback)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class PassthroughStage
|
29
|
+
include SomeStage
|
30
|
+
|
31
|
+
def process(message, callback)
|
32
|
+
callback.call(message)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ShouldNotBeReachedStage
|
37
|
+
include SomeStage
|
38
|
+
|
39
|
+
def process(message, callback)
|
40
|
+
raise "should not be reached but got #{message}!"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class ConsumeStage
|
45
|
+
include SomeStage
|
46
|
+
|
47
|
+
def process(message, callback)
|
48
|
+
message.consumed!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class RejectStage
|
53
|
+
include SomeStage
|
54
|
+
|
55
|
+
def process(message, callback)
|
56
|
+
message.rejected!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class BrokenMessageStage
|
61
|
+
include SomeStage
|
62
|
+
|
63
|
+
def process(message, callback)
|
64
|
+
message.broken!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -2,8 +2,8 @@ require 'json'
|
|
2
2
|
|
3
3
|
module EmPipelines
|
4
4
|
class AmqpEventSource
|
5
|
-
def initialize(queue, event_name)
|
6
|
-
@queue, @event_name = queue, event_name
|
5
|
+
def initialize(em, queue, event_name)
|
6
|
+
@em, @queue, @event_name = em, queue, event_name
|
7
7
|
end
|
8
8
|
|
9
9
|
def on_event(&handler)
|
@@ -13,10 +13,11 @@ module EmPipelines
|
|
13
13
|
def start!
|
14
14
|
@queue.subscribe do |header, json_payload|
|
15
15
|
message = Message.new ({
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
:header => header,
|
17
|
+
:origin => @queue.name,
|
18
|
+
:payload => JSON.parse(json_payload),
|
19
|
+
:event => @event_name,
|
20
|
+
:started_at => Time.now.to_i
|
20
21
|
})
|
21
22
|
@handler.call(message)
|
22
23
|
end
|
@@ -1,15 +1,11 @@
|
|
1
|
+
require 'empipelines/event_handlers'
|
2
|
+
|
1
3
|
module EmPipelines
|
2
4
|
class BatchEventSource
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def on_event(&handler)
|
8
|
-
@handler = handler
|
9
|
-
end
|
10
|
-
|
11
|
-
def on_batch_finished(&batch_finished_handler)
|
12
|
-
@batch_finished_handler = batch_finished_handler
|
5
|
+
include EventHandlers
|
6
|
+
|
7
|
+
def initialize(em, list_name, events)
|
8
|
+
@em, @list_name, @events = em, list_name,events
|
13
9
|
end
|
14
10
|
|
15
11
|
def start!
|
@@ -22,13 +18,16 @@ module EmPipelines
|
|
22
18
|
end
|
23
19
|
|
24
20
|
@events.each do |e|
|
25
|
-
message = Message.new({
|
21
|
+
message = Message.new({
|
22
|
+
:payload => e,
|
23
|
+
:origin => @list_name
|
24
|
+
})
|
26
25
|
|
27
26
|
message.on_rejected_broken(message_finished)
|
28
27
|
message.on_rejected(message_finished)
|
29
28
|
message.on_consumed(message_finished)
|
30
29
|
|
31
|
-
|
30
|
+
event_handler.call(message)
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
@@ -36,8 +35,8 @@ module EmPipelines
|
|
36
35
|
def check_if_finished
|
37
36
|
finished = (@finalised.size == @events.size)
|
38
37
|
|
39
|
-
if finished and
|
40
|
-
@em.next_tick {
|
38
|
+
if finished and finished_handler
|
39
|
+
@em.next_tick { finished_handler.call(@finalised) }
|
41
40
|
end
|
42
41
|
end
|
43
42
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module EmPipelines
|
2
|
+
module EventHandlers
|
3
|
+
def on_event(event_handler=nil, &block)
|
4
|
+
@event_handler = block_given? ? block : event_handler
|
5
|
+
end
|
6
|
+
|
7
|
+
def on_finished(batch_finished_handler=nil, &block)
|
8
|
+
@finished_handler = block_given? ? block : batch_finished_handler
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def finished_handler
|
13
|
+
@finished_handler
|
14
|
+
end
|
15
|
+
|
16
|
+
def event_handler
|
17
|
+
@event_handler
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'empipelines/event_handlers'
|
2
|
+
|
3
|
+
module EmPipelines
|
4
|
+
class IOEventSource
|
5
|
+
include EventHandlers
|
6
|
+
|
7
|
+
def initialize(em, file_path)
|
8
|
+
raise "File #{file_path} does not exist!" unless File.exists?(file_path)
|
9
|
+
@em, @file_path = em, file_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def start!
|
13
|
+
#TODO: this sucks hard, move to evented I/O
|
14
|
+
events = IO.readlines(@file_path).map { |e| e.strip }
|
15
|
+
|
16
|
+
wrapped_handler = BatchEventSource.new(@em, @file_path, events)
|
17
|
+
wrapped_handler.on_event(event_handler)
|
18
|
+
wrapped_handler.on_finished(finished_handler)
|
19
|
+
wrapped_handler.start!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/empipelines/message.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module EmPipelines
|
2
2
|
class PeriodicEventSource
|
3
|
-
def initialize(em, interval_in_secs, &event_sourcing_code)
|
4
|
-
@em
|
3
|
+
def initialize(em, name, interval_in_secs, &event_sourcing_code)
|
4
|
+
@em = em
|
5
|
+
@name = name
|
6
|
+
@interval_in_secs = interval_in_secs
|
7
|
+
@event_sourcing_code = event_sourcing_code
|
5
8
|
end
|
6
9
|
|
7
10
|
def start!
|
@@ -18,7 +21,8 @@ module EmPipelines
|
|
18
21
|
|
19
22
|
def tick!
|
20
23
|
event = @event_sourcing_code.call
|
21
|
-
|
24
|
+
|
25
|
+
@handler.call(Message.new(:payload => event, :origin => @name)) if event
|
22
26
|
end
|
23
27
|
end
|
24
28
|
end
|
data/lib/empipelines/pipeline.rb
CHANGED
@@ -6,8 +6,8 @@ module EmPipelines
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
9
|
+
def initialize(em, context, monitoring, logger)
|
10
|
+
@em = em
|
11
11
|
@logger = logger
|
12
12
|
@context = context
|
13
13
|
@monitoring = monitoring
|
@@ -20,7 +20,7 @@ module EmPipelines
|
|
20
20
|
logger = @logger
|
21
21
|
|
22
22
|
first_stage_process = stages.reverse.reduce(TerminatorStage) do |current_head, next_stage|
|
23
|
-
@
|
23
|
+
@em.spawn do |input|
|
24
24
|
begin
|
25
25
|
logger.debug "#{next_stage.class}#notify with #{input}}"
|
26
26
|
next_stage.call(input) do |output|
|
data/lib/empipelines.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module EmPipelines
|
2
|
-
VERSION = '0.2'
|
2
|
+
VERSION = '0.2.1'
|
3
3
|
end
|
4
4
|
|
5
|
+
require 'empipelines/event_handlers'
|
5
6
|
require 'empipelines/amqp_event_source'
|
6
7
|
require 'empipelines/batch_event_source'
|
8
|
+
require 'empipelines/io_event_source'
|
7
9
|
require 'empipelines/event_pipeline'
|
8
10
|
require 'empipelines/list_event_source'
|
9
11
|
require 'empipelines/message'
|
@@ -2,6 +2,7 @@ require 'empipelines/amqp_event_source'
|
|
2
2
|
|
3
3
|
module EmPipelines
|
4
4
|
class StubQueue
|
5
|
+
attr_accessor :name
|
5
6
|
def subscribe(&code)
|
6
7
|
@code = code
|
7
8
|
end
|
@@ -12,21 +13,25 @@ module EmPipelines
|
|
12
13
|
end
|
13
14
|
|
14
15
|
describe AmqpEventSource do
|
15
|
-
|
16
|
+
let (:em) { mock('eventmachine') }
|
17
|
+
|
18
|
+
it 'wraps each AMQP message and send to listeners' do
|
16
19
|
json_payload = '{"key":"value"}'
|
17
20
|
queue = StubQueue.new
|
21
|
+
queue.name = "this.is.some.queue"
|
18
22
|
event_type = "NuclearWar"
|
19
23
|
header = stub('header')
|
20
24
|
|
21
25
|
received_messages = []
|
22
26
|
|
23
|
-
amqp_source = AmqpEventSource.new(queue, event_type)
|
27
|
+
amqp_source = AmqpEventSource.new(em, queue, event_type)
|
24
28
|
amqp_source.on_event { |e| received_messages << e }
|
25
29
|
amqp_source.start!
|
26
30
|
|
27
31
|
queue.publish(header, json_payload)
|
28
32
|
|
29
33
|
received_messages.size.should eql(1)
|
34
|
+
received_messages.first[:origin].should ==(queue.name)
|
30
35
|
received_messages.first[:header].should ==(header)
|
31
36
|
received_messages.first[:payload].should ==({:key => "value"})
|
32
37
|
received_messages.first[:event].should ==(event_type)
|
@@ -9,9 +9,11 @@ module EmPipelines
|
|
9
9
|
em
|
10
10
|
end
|
11
11
|
|
12
|
+
let (:list_name) { "list of stuff" }
|
13
|
+
|
12
14
|
it "sends each element on the list as a payload to the listener" do
|
13
15
|
events = [1,2,3,4,5,6,7,8,9,10]
|
14
|
-
source = BatchEventSource.new(em, events)
|
16
|
+
source = BatchEventSource.new(em, list_name, events)
|
15
17
|
|
16
18
|
received = []
|
17
19
|
source.on_event do |e|
|
@@ -21,15 +23,16 @@ module EmPipelines
|
|
21
23
|
source.start!
|
22
24
|
|
23
25
|
received.map{ |i| i[:payload] }.should ==(events)
|
26
|
+
received.each{ |i| i[:origin].should == list_name }
|
24
27
|
end
|
25
28
|
|
26
29
|
it "calls the batch finished callback when all items were processed" do
|
27
30
|
events = [1,2,3,4,5,6,7,8,9,10]
|
28
|
-
source = BatchEventSource.new(em, events)
|
31
|
+
source = BatchEventSource.new(em, list_name, events)
|
29
32
|
|
30
33
|
has_finished = []
|
31
34
|
|
32
|
-
source.
|
35
|
+
source.on_finished do |messages|
|
33
36
|
has_finished << messages
|
34
37
|
end
|
35
38
|
|
@@ -42,11 +45,11 @@ module EmPipelines
|
|
42
45
|
has_finished.first.map{ |i| i[:payload] }.should ==(events)
|
43
46
|
end
|
44
47
|
|
45
|
-
it "finishes
|
46
|
-
source = BatchEventSource.new(em, [])
|
48
|
+
it "finishes immediately if there are no events to process" do
|
49
|
+
source = BatchEventSource.new(em, list_name, [])
|
47
50
|
|
48
51
|
has_finished = []
|
49
|
-
source.
|
52
|
+
source.on_finished do |messages|
|
50
53
|
has_finished << true
|
51
54
|
end
|
52
55
|
|
@@ -59,11 +62,11 @@ module EmPipelines
|
|
59
62
|
has_finished.first.should be_true
|
60
63
|
end
|
61
64
|
|
62
|
-
it "
|
65
|
+
it "only calls the finished handler if all events were processed" do
|
63
66
|
events = [1,2,3,4,5,6,7,8,9,10]
|
64
|
-
source = BatchEventSource.new(em, events)
|
67
|
+
source = BatchEventSource.new(em, list_name, events)
|
65
68
|
|
66
|
-
source.
|
69
|
+
source.on_finished do |messages|
|
67
70
|
raise "should not be called"
|
68
71
|
end
|
69
72
|
|
File without changes
|
File without changes
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'empipelines/io_event_source'
|
2
|
+
|
3
|
+
module EmPipelines
|
4
|
+
describe IOEventSource do
|
5
|
+
let(:events_file) { File.join(File.dirname(__FILE__), 'io_event_source.dat')}
|
6
|
+
let(:empty_file) { File.join(File.dirname(__FILE__), 'empty_io_event_source.dat')}
|
7
|
+
let(:inexistent_file) { File.join(File.dirname(__FILE__), 'not_really_son.dat')}
|
8
|
+
let (:em) do
|
9
|
+
em = mock('eventmachine')
|
10
|
+
em.stub(:next_tick).and_yield
|
11
|
+
em
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'verifies file existance' do
|
15
|
+
lambda{ IOEventSource.new(em, inexistent_file) }.should raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sends each line in the file as a message to listeners' do
|
19
|
+
source = IOEventSource.new(em, events_file)
|
20
|
+
|
21
|
+
received = []
|
22
|
+
source.on_event do |e|
|
23
|
+
received << e
|
24
|
+
end
|
25
|
+
|
26
|
+
source.start!
|
27
|
+
|
28
|
+
received.map{ |i| i[:payload].to_i }.should ==([1,2,3])
|
29
|
+
received.each{ |i| i[:origin].should == events_file }
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it 'calls the finished callback when all messages were processed' do
|
34
|
+
source = IOEventSource.new(em, events_file)
|
35
|
+
|
36
|
+
has_finished = []
|
37
|
+
|
38
|
+
source.on_finished do |messages|
|
39
|
+
has_finished << messages
|
40
|
+
end
|
41
|
+
|
42
|
+
source.on_event do |e|
|
43
|
+
e.consumed!
|
44
|
+
end
|
45
|
+
|
46
|
+
source.start!
|
47
|
+
|
48
|
+
has_finished.first.map{ |i| i[:payload].to_i }.should ==([1,2,3])
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'finishes immediately if there are no events to process' do
|
52
|
+
source = IOEventSource.new(em, empty_file)
|
53
|
+
|
54
|
+
has_finished = []
|
55
|
+
source.on_finished do |messages|
|
56
|
+
has_finished << true
|
57
|
+
end
|
58
|
+
|
59
|
+
source.on_event do |e|
|
60
|
+
raise 'should not be called!'
|
61
|
+
end
|
62
|
+
|
63
|
+
source.start!
|
64
|
+
|
65
|
+
has_finished.first.should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'only calls the finished handler if all events were processed' do
|
69
|
+
source = IOEventSource.new(em, events_file)
|
70
|
+
|
71
|
+
source.on_finished do |messages|
|
72
|
+
raise "should not be called"
|
73
|
+
end
|
74
|
+
|
75
|
+
count = 0
|
76
|
+
source.on_event do |e|
|
77
|
+
e.consumed! if (count=+1) > 1
|
78
|
+
end
|
79
|
+
|
80
|
+
source.start!
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can read really long files' do
|
84
|
+
pending('current implementation is terribly naive')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'empipelines/periodic_event_source'
|
2
|
+
|
3
|
+
module EmPipelines
|
4
|
+
describe PeriodicEventSource do
|
5
|
+
it 'schedules itself with eventmachine' do
|
6
|
+
em = stub('eventmachine')
|
7
|
+
em.should_receive(:add_periodic_timer).with(5)
|
8
|
+
PeriodicEventSource.new(em, 'a name', 5){ 'something cool!' }.start!
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'sends the result of the periodic action to the handler' do
|
12
|
+
name = 'some name'
|
13
|
+
event = {:this => :is, :some => 'event'}
|
14
|
+
received_messages = []
|
15
|
+
|
16
|
+
source = PeriodicEventSource.new(stub('eventmachine'), name, 666){ event }
|
17
|
+
source.on_event { |msg| received_messages << msg}
|
18
|
+
source.tick!
|
19
|
+
|
20
|
+
received_messages.should have(1).item
|
21
|
+
received_messages[0][:payload].should eql(event)
|
22
|
+
received_messages[0][:origin].should eql(name)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'doesnt o anything if no event was generated' do
|
26
|
+
source = PeriodicEventSource.new(stub('eventmachine'), 'a name', 666){ nil }
|
27
|
+
source.on_event { |m| raise 'should not be called' }
|
28
|
+
source.tick!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
File without changes
|
data/unit/spec_helper.rb
ADDED
File without changes
|
File without changes
|
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:
|
4
|
+
version: 0.2.1
|
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-21 00:00:00.000000000Z
|
14
14
|
dependencies: []
|
15
15
|
description: Simple Event Handling Pipeline Architecture for EventMachine
|
16
16
|
email: pcalcado+empipelines@gmail.com
|
@@ -24,23 +24,31 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- Rakefile
|
26
26
|
- empipelines.gemspec
|
27
|
+
- functional/consuming_events_from_file_spec.rb
|
28
|
+
- functional/events.dat
|
29
|
+
- functional/test_stages.rb
|
27
30
|
- lib/empipelines.rb
|
28
31
|
- lib/empipelines/amqp_event_source.rb
|
29
32
|
- lib/empipelines/batch_event_source.rb
|
33
|
+
- lib/empipelines/event_handlers.rb
|
30
34
|
- lib/empipelines/event_pipeline.rb
|
35
|
+
- lib/empipelines/io_event_source.rb
|
31
36
|
- lib/empipelines/list_event_source.rb
|
32
37
|
- lib/empipelines/message.rb
|
33
38
|
- lib/empipelines/periodic_event_source.rb
|
34
39
|
- lib/empipelines/pipeline.rb
|
35
|
-
-
|
36
|
-
-
|
37
|
-
-
|
38
|
-
-
|
39
|
-
-
|
40
|
-
-
|
41
|
-
-
|
42
|
-
-
|
43
|
-
-
|
40
|
+
- unit/empipelines/amqp_event_source_spec.rb
|
41
|
+
- unit/empipelines/batch_event_source_spec.rb
|
42
|
+
- unit/empipelines/empty_io_event_source.dat
|
43
|
+
- unit/empipelines/event_pipeline_spec.rb
|
44
|
+
- unit/empipelines/io_event_source.dat
|
45
|
+
- unit/empipelines/io_event_source_spec.rb
|
46
|
+
- unit/empipelines/list_event_source_spec.rb
|
47
|
+
- unit/empipelines/message_spec.rb
|
48
|
+
- unit/empipelines/periodic_event_source_spec.rb
|
49
|
+
- unit/empipelines/pipeline_spec.rb
|
50
|
+
- unit/spec_helper.rb
|
51
|
+
- unit/stage_helper.rb
|
44
52
|
homepage: http://github.com/pcalcado/empipelines
|
45
53
|
licenses: []
|
46
54
|
post_install_message:
|
@@ -56,7 +64,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
64
|
version: '0'
|
57
65
|
segments:
|
58
66
|
- 0
|
59
|
-
hash:
|
67
|
+
hash: -3827854829014823938
|
60
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
69
|
none: false
|
62
70
|
requirements:
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'empipelines/periodic_event_source'
|
2
|
-
|
3
|
-
module EmPipelines
|
4
|
-
describe PeriodicEventSource do
|
5
|
-
it 'schedules itself with eventmachine' do
|
6
|
-
em = stub('eventmachine')
|
7
|
-
em.should_receive(:add_periodic_timer).with(5)
|
8
|
-
PeriodicEventSource.new(em, 5){ "something cool!" }.start!
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'sends the result of the periodic action to the handler' do
|
12
|
-
expected_message = { :something => "goes here" }
|
13
|
-
received_messages = []
|
14
|
-
|
15
|
-
source = PeriodicEventSource.new(stub('eventmachine'), 666){ expected_message }
|
16
|
-
source.on_event { |msg| received_messages << msg}
|
17
|
-
source.tick!
|
18
|
-
|
19
|
-
received_messages.should eql([expected_message])
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'doesnt o anything if no event was generated' do
|
23
|
-
source = PeriodicEventSource.new(stub('eventmachine'), 666){ nil }
|
24
|
-
source.on_event { |m| raise "should not be called" }
|
25
|
-
source.tick!
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|