empipelines 0.2.2 → 0.2.3

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 CHANGED
@@ -4,10 +4,8 @@
4
4
  * Make wiring easier
5
5
  * Example apps
6
6
  * Control flow for AmqpEventSource
7
- * Transaction ID on each message
8
7
  * Evented I/O for IOEventSource
9
8
  * Consolidate logger and monitoring
10
9
  * Default monitoring implementation
11
10
  * Performance testing
12
- * Make all events composable (e.g. new_on_event = old_on_event o new_handler)
13
11
  * Detect insonsistency when handler didnt consume or pass message ahead
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.2'
8
- s.date = '2011-12-23'
7
+ s.version = '0.2.3'
8
+ s.date = '2012-01-02'
9
9
  s.rubyforge_project = 'empipelines'
10
10
 
11
11
  s.summary = "Simple Event Handling Pipeline Architecture for EventMachine"
@@ -45,10 +45,9 @@ Gem::Specification.new do |s|
45
45
  lib/empipelines/aggregated_event_source.rb
46
46
  lib/empipelines/amqp_event_source.rb
47
47
  lib/empipelines/batch_event_source.rb
48
- lib/empipelines/event_handlers.rb
49
48
  lib/empipelines/event_pipeline.rb
49
+ lib/empipelines/event_source.rb
50
50
  lib/empipelines/io_event_source.rb
51
- lib/empipelines/list_event_source.rb
52
51
  lib/empipelines/message.rb
53
52
  lib/empipelines/periodic_event_source.rb
54
53
  lib/empipelines/pipeline.rb
@@ -57,9 +56,9 @@ Gem::Specification.new do |s|
57
56
  unit/empipelines/batch_event_source_spec.rb
58
57
  unit/empipelines/empty_io_event_source.dat
59
58
  unit/empipelines/event_pipeline_spec.rb
59
+ unit/empipelines/event_source_spec.rb
60
60
  unit/empipelines/io_event_source.dat
61
61
  unit/empipelines/io_event_source_spec.rb
62
- unit/empipelines/list_event_source_spec.rb
63
62
  unit/empipelines/message_spec.rb
64
63
  unit/empipelines/periodic_event_source_spec.rb
65
64
  unit/empipelines/pipeline_spec.rb
@@ -1,8 +1,7 @@
1
- require 'empipelines/event_handlers'
1
+ require 'empipelines/event_source'
2
2
 
3
3
  module EmPipelines
4
- class AggregatedEventSource
5
- include EventHandlers
4
+ class AggregatedEventSource < EventSource
6
5
 
7
6
  def initialize(em, *event_sources)
8
7
  @em, @sources = em, event_sources.flatten
@@ -15,7 +14,7 @@ module EmPipelines
15
14
 
16
15
  s.on_finished do |*ignored|
17
16
  finished += 1
18
- finished_handler.call(self) if finished == @sources.size
17
+ finished! if finished == @sources.size
19
18
  end
20
19
  @em.next_tick { s.start! }
21
20
  end
@@ -1,26 +1,26 @@
1
+ require 'empipelines/event_source'
2
+ require 'empipelines/message'
3
+
1
4
  require 'json'
2
5
 
3
6
  module EmPipelines
4
7
  #this must have a on_finished!
5
- class AmqpEventSource
8
+ class AmqpEventSource < EventSource
9
+
6
10
  def initialize(em, queue, event_name)
7
11
  @em, @queue, @event_name = em, queue, event_name
8
12
  end
9
13
 
10
- def on_event(&handler)
11
- @handler = handler
12
- end
13
-
14
14
  def start!
15
15
  @queue.subscribe do |header, json_payload|
16
- message = Message.new ({
17
- :header => header,
18
- :origin => @queue.name,
19
- :payload => JSON.parse(json_payload),
20
- :event => @event_name,
21
- :started_at => Time.now.to_i
22
- })
23
- @handler.call(message)
16
+ message = Message.new({
17
+ :header => header,
18
+ :origin => @queue.name,
19
+ :payload => JSON.parse(json_payload),
20
+ :event => @event_name,
21
+ :started_at => Time.now.to_i
22
+ })
23
+ event!(message)
24
24
  end
25
25
  end
26
26
  end
@@ -1,8 +1,8 @@
1
- require 'empipelines/event_handlers'
1
+ require 'empipelines/message'
2
+ require 'empipelines/event_source'
2
3
 
3
4
  module EmPipelines
4
- class BatchEventSource
5
- include EventHandlers
5
+ class BatchEventSource < EventSource
6
6
 
7
7
  def initialize(em, list_name, events)
8
8
  @num_finalised = 0
@@ -24,11 +24,11 @@ module EmPipelines
24
24
  :origin => @list_name
25
25
  })
26
26
 
27
- message.on_rejected_broken(message_finished)
27
+ message.on_broken(message_finished)
28
28
  message.on_rejected(message_finished)
29
29
  message.on_consumed(message_finished)
30
30
 
31
- event_handler.call(message)
31
+ event!(message)
32
32
  end
33
33
  end
34
34
 
@@ -37,11 +37,7 @@ module EmPipelines
37
37
  #TODO: can we make this not be based on size?
38
38
  #it makes it harder to have streams as event sources (i.e. ranges).
39
39
  #this class should only rely on Enumerable methods.
40
- finished = (@num_finalised == @events.size)
41
-
42
- if finished and finished_handler
43
- @em.next_tick { finished_handler.call(self) }
44
- end
40
+ finished! if (@num_finalised == @events.size)
45
41
  end
46
42
  end
47
43
  end
@@ -0,0 +1,67 @@
1
+ module EmPipelines
2
+ class EventSource
3
+ def on_event(handler=nil, &block)
4
+ add_handlers!(event_handler, (handler || block))
5
+ end
6
+
7
+ def on_finished(handler=nil, &block)
8
+ add_handlers!(finished_handler, (handler || block))
9
+ end
10
+
11
+ protected
12
+ def event_handler
13
+ @event_handler ||= []
14
+ @event_handler
15
+ end
16
+
17
+ def finished_handler
18
+ @finished_handler ||= []
19
+ @finished_handler
20
+ end
21
+
22
+ def finished!
23
+ finished_handler.each{ |h| h.call(self) }
24
+ end
25
+
26
+ def event!(msg)
27
+ if event_handler.size == 1 then
28
+ event_handler.first.call(msg)
29
+ else
30
+ copies = a_copy_per_handler(msg)
31
+ event_handler.each do |h|
32
+ h.call(copies.pop)
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+ def a_copy_per_handler(msg)
39
+ msg_copies = event_handler.map { |x| msg.copy }
40
+
41
+ verify_copies = lambda do |m|
42
+ if msg_copies.all?(&:processed?) then
43
+ if msg_copies.any? { |m| m.state == :broken } then
44
+ msg.broken!
45
+ elsif msg_copies.any? { |m| m.state == :rejected } then
46
+ msg.rejected!
47
+ else
48
+ msg.consumed!
49
+ end
50
+ end
51
+ end
52
+
53
+ msg_copies.each do |copy|
54
+ copy.on_consumed(verify_copies)
55
+ copy.on_rejected(verify_copies)
56
+ copy.on_broken(verify_copies)
57
+ end
58
+
59
+ msg_copies.clone
60
+ end
61
+
62
+ def add_handlers!(handler_list, new_handlers)
63
+ to_add = new_handlers.is_a?(Enumerable) ? new_handlers : [new_handlers]
64
+ to_add.each { |h| handler_list << h }
65
+ end
66
+ end
67
+ end
@@ -1,8 +1,7 @@
1
- require 'empipelines/event_handlers'
1
+ require 'empipelines/event_source'
2
2
 
3
3
  module EmPipelines
4
- class IOEventSource
5
- include EventHandlers
4
+ class IOEventSource < EventSource
6
5
 
7
6
  def initialize(em, file_path)
8
7
  raise "File #{file_path} does not exist!" unless File.exists?(file_path)
@@ -15,7 +14,7 @@ module EmPipelines
15
14
 
16
15
  wrapped_handler = BatchEventSource.new(@em, @file_path, events)
17
16
  wrapped_handler.on_event(event_handler)
18
- wrapped_handler.on_finished { |*ignored| finished_handler.call(self) }
17
+ wrapped_handler.on_finished { |*ignored| finished! }
19
18
  wrapped_handler.start!
20
19
  end
21
20
  end
@@ -1,8 +1,12 @@
1
1
  module EmPipelines
2
2
  class Message
3
- attr_reader :state
4
-
5
- def initialize(base_hash={})
3
+ attr_reader :state, :co_id
4
+
5
+ @@count = 0
6
+
7
+ def initialize(base_hash={}, origin=nil)
8
+ @origin = origin
9
+ create_correlation_id!
6
10
  backing_hash!(base_hash)
7
11
  created!
8
12
  end
@@ -28,15 +32,15 @@ module EmPipelines
28
32
  end
29
33
 
30
34
  def on_consumed(callback=nil, &callback_block)
31
- @consumed_callback = block_given? ? callback_block : callback
35
+ @consumed_callback = (callback || callback_block)
32
36
  end
33
37
 
34
38
  def on_rejected(callback=nil, &callback_block)
35
- @rejected_callback = block_given? ? callback_block : callback
39
+ @rejected_callback = (callback || callback_block)
36
40
  end
37
-
38
- def on_rejected_broken(callback=nil, &callback_block)
39
- @rejected_broken_callback = block_given? ? callback_block : callback
41
+
42
+ def on_broken(callback=nil, &callback_block)
43
+ @broken_callback = (callback || callback_block)
40
44
  end
41
45
 
42
46
  def consumed!
@@ -50,11 +54,15 @@ module EmPipelines
50
54
  @state = :rejected
51
55
  invoke(@rejected_callback)
52
56
  end
53
-
57
+
54
58
  def broken!
55
59
  check_if_mutation_allowed
56
- @state = :rejected_broken
57
- invoke(@rejected_broken_callback)
60
+ @state = :broken
61
+ invoke(@broken_callback)
62
+ end
63
+
64
+ def processed?
65
+ @state != :created
58
66
  end
59
67
 
60
68
  def as_hash
@@ -64,20 +72,33 @@ module EmPipelines
64
72
  def payload
65
73
  as_hash[:payload]
66
74
  end
67
-
75
+
76
+ def copy
77
+ forked = Message.new(as_hash, self)
78
+ forked.on_broken(@broken_callback)
79
+ forked.on_rejected(@rejected_callback)
80
+ forked.on_consumed(@consumed_callback)
81
+ forked
82
+ end
83
+
68
84
  def to_s
69
- "#{self.class.name} state:#{@state} backing_hash:#{as_hash}"
85
+ "#{self.class.name} co_id:#{co_id} state:#{@state} backing_hash:#{as_hash}"
70
86
  end
71
87
 
72
88
  private
89
+ def create_correlation_id!
90
+ @@count += 1
91
+ suffix = @origin.nil? ? "@#{Process.pid}" : @origin.co_id
92
+ @co_id = "#{@@count}@#{suffix}"
93
+ end
73
94
 
74
95
  def backing_hash!(other)
75
96
  @backing_hash = symbolised(other)
76
97
  end
77
-
98
+
78
99
  def symbolised(raw_hash)
79
100
  raw_hash.reduce({}) do |acc, (key, value)|
80
- acc[key.to_s.to_sym] = value.is_a?(Hash) ? symbolised(value) : value
101
+ acc[key.to_s.to_sym] = value.is_a?(Hash) ? symbolised(value) : value
81
102
  acc
82
103
  end
83
104
  end
@@ -85,11 +106,11 @@ module EmPipelines
85
106
  def created!
86
107
  @state = :created
87
108
  end
88
-
109
+
89
110
  def check_if_mutation_allowed
90
- raise "Cannot mutate #{self}" unless @state == :created
111
+ raise "Cannot mutate #{self}" if processed?
91
112
  end
92
-
113
+
93
114
  def invoke(callback)
94
115
  callback.call(self) if callback
95
116
  end
@@ -1,5 +1,5 @@
1
1
  module EmPipelines
2
- class PeriodicEventSource
2
+ class PeriodicEventSource < EventSource
3
3
  #on finish!!!!
4
4
  def initialize(em, name, interval_in_secs, &event_sourcing_code)
5
5
  @em = em
@@ -16,14 +16,10 @@ module EmPipelines
16
16
  end
17
17
  end
18
18
 
19
- def on_event(&handler)
20
- @handler = handler
21
- end
22
-
23
19
  def tick!
24
20
  event = @event_sourcing_code.call
25
21
 
26
- @handler.call(Message.new(:payload => event, :origin => @name)) if event
22
+ event!(Message.new(:payload => event, :origin => @name)) if event
27
23
  end
28
24
  end
29
25
  end
data/lib/empipelines.rb CHANGED
@@ -1,14 +1,13 @@
1
1
  module EmPipelines
2
- VERSION = '0.2.2'
2
+ VERSION = '0.2.3'
3
3
  end
4
4
 
5
5
  require 'empipelines/message'
6
6
 
7
- require 'empipelines/event_handlers'
7
+ require 'empipelines/event_source'
8
8
  require 'empipelines/amqp_event_source'
9
9
  require 'empipelines/batch_event_source'
10
10
  require 'empipelines/io_event_source'
11
- require 'empipelines/list_event_source'
12
11
  require 'empipelines/periodic_event_source'
13
12
  require 'empipelines/aggregated_event_source'
14
13
 
@@ -1,17 +1,15 @@
1
1
  require 'empipelines/aggregated_event_source'
2
2
 
3
3
  module EmPipelines
4
- class EventSourceStub
5
- include EventHandlers
6
-
7
- def event!(contents)
4
+ class EventSourceStub < EventSource
5
+ def event_now!(contents)
8
6
  raise 'not started' unless @started
9
- event_handler.call(contents) if event_handler
7
+ event!(contents)
10
8
  end
11
9
 
12
- def finish!
10
+ def finish_now!
13
11
  raise 'not started' unless @started
14
- finished_handler.call([:this, :should, :not, :be, :used])
12
+ finished!
15
13
  end
16
14
 
17
15
  def start!
@@ -40,11 +38,11 @@ module EmPipelines
40
38
 
41
39
  aggregated.start!
42
40
 
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]
41
+ source1.event_now! expected[0]
42
+ source2.event_now! expected[1]
43
+ source2.event_now! expected[2]
44
+ source3.event_now! expected[3]
45
+ source1.event_now! expected[4]
48
46
 
49
47
  received.should ==(expected)
50
48
  end
@@ -61,9 +59,9 @@ module EmPipelines
61
59
  end
62
60
 
63
61
  aggregated.start!
64
- sources[2].finish!
65
- sources[1].finish!
66
- sources[0].finish!
62
+ sources[2].finish_now!
63
+ sources[1].finish_now!
64
+ sources[0].finish_now!
67
65
 
68
66
  has_finished.first.should be_true
69
67
  end
@@ -78,8 +76,8 @@ module EmPipelines
78
76
  end
79
77
 
80
78
  aggregated.start!
81
- sources[2].finish!
82
- sources[0].finish!
79
+ sources[2].finish_now!
80
+ sources[0].finish_now!
83
81
  end
84
82
  end
85
83
  end
@@ -1,7 +1,7 @@
1
1
  require 'empipelines/batch_event_source'
2
2
 
3
3
  module EmPipelines
4
- ShouldNotBeCalled = lambda { raise 'should not be called' }
4
+ ShouldNotBeCalled = lambda { |*x| raise 'should not be called' }
5
5
  describe BatchEventSource do
6
6
 
7
7
  let (:em) do
@@ -56,7 +56,7 @@ module EmPipelines
56
56
  has_finished << true
57
57
  end
58
58
 
59
- source.on_event(&ShouldNotBeCalled)
59
+ source.on_event(ShouldNotBeCalled)
60
60
 
61
61
  source.start!
62
62
 
@@ -67,8 +67,6 @@ module EmPipelines
67
67
  events = [1,2,3,4,5,6,7,8,9,10]
68
68
  source = BatchEventSource.new(em, list_name, events)
69
69
 
70
- source.on_event(&ShouldNotBeCalled)
71
-
72
70
  count = 0
73
71
  source.on_event do |e|
74
72
  e.consumed! if (count=+1) > 1
@@ -0,0 +1,115 @@
1
+ require 'empipelines/event_source'
2
+
3
+ module EmPipelines
4
+ class StubEventSource < EventSource
5
+ def event_now!(message)
6
+ event!(message)
7
+ end
8
+
9
+ def finish_now!
10
+ finished!
11
+ end
12
+ end
13
+
14
+ describe EventSource do
15
+ context 'calling event handlers' do
16
+ it 'calls the handler when an message is to be processed' do
17
+ message = stub('message')
18
+ received = []
19
+
20
+ source = StubEventSource.new
21
+ source.on_event { |a| received << a }
22
+ source.event_now!(message)
23
+ received.should==([message])
24
+ end
25
+
26
+ it 'calls the finished handler when all events were processed' do
27
+ message = stub('message')
28
+ received = []
29
+
30
+ source = StubEventSource.new
31
+ source.on_finished { |a| received << a }
32
+ source.finish_now!
33
+ received.should==([source])
34
+ end
35
+
36
+ it 'does not do anything if no handlers' do
37
+ StubEventSource.new.event_now!({})
38
+ StubEventSource.new.finish_now!
39
+ end
40
+ end
41
+
42
+ context 'multiple event handlers' do
43
+ let(:message1) { Message.new({:a => 1}) }
44
+ let(:message2) { Message.new({:b => 2}) }
45
+
46
+ def mark_as(state)
47
+ lambda {|m| m.send "#{state}!".to_sym }
48
+ end
49
+
50
+ def should_be_marked_as(desired, message)
51
+ undesired = [:broken, :consumed, :rejected] - [desired]
52
+ undesired.each { |u| message.should_not_receive("#{u}!".to_sym) }
53
+ message.should_receive("#{desired}!".to_sym)
54
+ message.should_receive(:copy).at_least(:once).and_return { Message.new({}) }
55
+ end
56
+
57
+ it 'supports multiple handlers for a single event'do
58
+ received = []
59
+ finished = []
60
+
61
+ source = StubEventSource.new
62
+ source.on_event([lambda {|m| received << [1, m.payload]}, lambda {|m| received << [2, m.payload]}])
63
+ source.on_event {|m| received << [3, m.payload]}
64
+
65
+ source.on_finished {|s| finished << [10, s]}
66
+ source.on_finished([lambda {|s| finished << [20, s]}, lambda {|s| finished << [30, s]}])
67
+
68
+ source.event_now!(message1)
69
+ source.event_now!(message2)
70
+ source.finish_now!
71
+
72
+ received.should ==([
73
+ [1, message1.payload],
74
+ [2, message1.payload],
75
+ [3, message1.payload],
76
+ [1, message2.payload],
77
+ [2, message2.payload],
78
+ [3, message2.payload]
79
+ ])
80
+
81
+ finished.should ==([[10, source], [20, source], [30, source]])
82
+ end
83
+
84
+ it 'marks the message as consumed if all handlers consume' do
85
+ message = mock('message')
86
+ should_be_marked_as(:consumed, message)
87
+
88
+ source = StubEventSource.new
89
+ source.on_event([mark_as(:consumed), mark_as(:consumed), mark_as(:consumed)])
90
+ source.event_now!(message)
91
+ end
92
+
93
+ it 'marks the message as rejects if at least one handler rejects and all others consumed' do
94
+ message = mock('message')
95
+ should_be_marked_as(:rejected, message)
96
+
97
+ source = StubEventSource.new
98
+ source.on_event([mark_as(:consumed), mark_as(:consumed), mark_as(:rejected), mark_as(:consumed)])
99
+ source.event_now!(message)
100
+ end
101
+
102
+ it 'marks message as broken if at least one handler marks as broken, regardless of others' do
103
+ message = mock('message')
104
+ should_be_marked_as(:broken, message)
105
+
106
+ source = StubEventSource.new
107
+ source.on_event([mark_as(:consumed), mark_as(:consumed), mark_as(:rejected), mark_as(:consumed), mark_as(:broken)])
108
+ source.event_now!(message)
109
+ end
110
+ end
111
+
112
+ context 'flow control' do
113
+ end
114
+ end
115
+ end
@@ -1,4 +1,5 @@
1
1
  require 'empipelines/io_event_source'
2
+ require 'empipelines/batch_event_source'
2
3
 
3
4
  module EmPipelines
4
5
  describe IOEventSource do
@@ -2,8 +2,8 @@ require 'empipelines/message'
2
2
 
3
3
  module EmPipelines
4
4
  describe Message do
5
- context "mostly behaves like a hashmap" do
6
- it "stores values under symbolised keys" do
5
+ context 'mostly behaves like a hashmap' do
6
+ it 'stores values under symbolised keys' do
7
7
  original_hash = {:a => 1, :b => 2}
8
8
 
9
9
  message = Message.new(original_hash)
@@ -13,23 +13,23 @@ module EmPipelines
13
13
  message[:doesntexist].should ==(original_hash[:doesntexist])
14
14
  end
15
15
 
16
- it "symbolises keys of all maps in the message" do
16
+ it 'symbolises keys of all maps in the message' do
17
17
  message = Message.new({
18
18
  :a => 1,
19
- "b" => 2,
19
+ 'b' => 2,
20
20
  3 => 3 ,
21
- "d" => {
22
- "d1" => {"e" => 5},
23
- "d2" => nil}
21
+ 'd' => {
22
+ 'd1' => {'e' => 5},
23
+ 'd2' => nil}
24
24
  })
25
25
  message[:a].should ==(1)
26
26
  message[:b].should ==(2)
27
- message["3".to_sym].should ==(3)
27
+ message['3'.to_sym].should ==(3)
28
28
  message[:d][:d1][:e].should ==(5)
29
29
  message[:d][:d2].should be_nil
30
30
  end
31
-
32
- it "allows for values to be CRUD" do
31
+
32
+ it 'allows for values to be CRUD' do
33
33
  original_hash = {:a => 1, :b => 2, :c => 0}
34
34
 
35
35
  message = Message.new(original_hash)
@@ -41,10 +41,10 @@ module EmPipelines
41
41
  message[:a].should ==(666)
42
42
  message[:b].should be_nil
43
43
  message[:c].should ==(original_hash[:c])
44
- message[:z].should ==(999)
44
+ message[:z].should ==(999)
45
45
  end
46
46
 
47
- it "can be merged with a map, symbolising keys" do
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
 
@@ -53,74 +53,94 @@ module EmPipelines
53
53
  end
54
54
  end
55
55
 
56
- context "message status" do
56
+ context 'message status handlers' do
57
57
 
58
- let (:handler_that_should_never_be_called) { lambda { raise "This shouldnt happen" } }
58
+ let (:handler_that_should_never_be_called) { lambda { raise 'This shouldnt happen' } }
59
59
 
60
- it "doesnt do anything if no state callback specified" do
60
+ it 'doesnt do anything if no state callback specified' do
61
61
  Message.new.consumed!
62
62
  Message.new.rejected!
63
- Message.new.broken!
63
+ Message.new.broken!
64
+ end
65
+
66
+ it 'is possible to override a handler' do
67
+ origin = Message.new
68
+ origin.on_broken(handler_that_should_never_be_called)
69
+ origin.on_rejected(handler_that_should_never_be_called)
70
+ origin.on_consumed(handler_that_should_never_be_called)
71
+
72
+ consume = origin.copy
73
+ consume.on_consumed {}
74
+
75
+ reject = origin.copy
76
+ reject.on_rejected {}
77
+
78
+ broken = origin.copy
79
+ broken.on_broken {}
80
+
81
+ consume.consumed!
82
+ broken.broken!
83
+ reject.rejected!
64
84
  end
65
85
 
66
- it "is possible to reject a message if broken"do
86
+ it 'is possible to reject a message if broken'do
67
87
  called = []
68
-
69
- message = Message.new
70
- message.on_rejected_broken do |msg|
88
+
89
+ message = Message.new
90
+ message.on_broken do |msg|
71
91
  called << msg
72
92
  end
73
-
74
- message.on_rejected(handler_that_should_never_be_called)
93
+
94
+ message.on_rejected(handler_that_should_never_be_called)
75
95
  message.on_consumed(handler_that_should_never_be_called)
76
96
 
77
97
  message.broken!
78
-
98
+
79
99
  called.should==([message])
80
100
  end
81
-
82
- it "is possible to reject a message if consumer cant handle it" do
101
+
102
+ it 'is possible to reject a message if consumer cant handle it' do
83
103
  called = []
84
-
85
- message = Message.new
104
+
105
+ message = Message.new
86
106
  message.on_rejected do |msg|
87
107
  called << msg
88
108
  end
89
-
90
- message.on_rejected_broken(handler_that_should_never_be_called)
109
+
110
+ message.on_broken(handler_that_should_never_be_called)
91
111
  message.on_consumed(handler_that_should_never_be_called)
92
112
 
93
113
  message.rejected!
94
-
114
+
95
115
  called.should==([message])
96
116
  end
97
117
 
98
- it "is possible to mark a message as consumed" do
118
+ it 'is possible to mark a message as consumed' do
99
119
  called = []
100
-
101
- message = Message.new
120
+
121
+ message = Message.new
102
122
  message.on_consumed do |msg|
103
123
  called << msg
104
124
  end
105
-
106
- message.on_rejected_broken(handler_that_should_never_be_called)
125
+
126
+ message.on_broken(handler_that_should_never_be_called)
107
127
  message.on_rejected(handler_that_should_never_be_called)
108
128
 
109
129
  message.consumed!
110
-
130
+
111
131
  called.should==([message])
112
132
  end
113
-
114
- it "is not possible to change a message after marking as consumed or rejected" do
133
+
134
+ it 'is not possible to change a message after marking as consumed or rejected' do
115
135
  read = lambda { |m| m[:some_key] }
116
136
  mutate = lambda { |m| m[:some_key] = :some_value }
117
-
137
+
118
138
  consumed = Message.new
119
139
  consumed.consumed!
120
-
140
+
121
141
  rejected = Message.new
122
142
  rejected.rejected!
123
-
143
+
124
144
  broken = Message.new
125
145
  broken.broken!
126
146
 
@@ -132,5 +152,104 @@ module EmPipelines
132
152
  lambda{ mutate.call(broken) }.should raise_error
133
153
  end
134
154
  end
155
+
156
+ context 'cloning messages' do
157
+ it 'copys a message with equal initial state' do
158
+ origin = Message.new({:a => 1})
159
+ copy = origin.copy
160
+
161
+ origin.as_hash.should ==(copy.as_hash)
162
+ end
163
+
164
+ it 'copys a message with equal handlers' do
165
+ origin = Message.new({:a => 1})
166
+
167
+ consumed = []
168
+ rejected = []
169
+ broken = []
170
+
171
+ origin.on_consumed { |m| consumed << m}
172
+ origin.on_rejected { |m| rejected << m}
173
+ origin.on_broken { |m| broken << m}
174
+
175
+ copy1 = origin.copy
176
+ copy2 = origin.copy
177
+
178
+ origin.consumed!
179
+ copy1.broken!
180
+ copy2.rejected!
181
+
182
+ consumed.should ==([origin])
183
+ rejected.should ==([copy2])
184
+ broken.should ==([copy1])
185
+ end
186
+
187
+ it 'makes the messages contents independent' do
188
+ origin = Message.new({:a => 1})
189
+ copy = origin.copy
190
+ origin[:b] = 2
191
+ copy[:c] = 3
192
+
193
+ origin[:c].should be_nil
194
+ copy[:c].should_not be_nil
195
+
196
+ origin[:b].should_not be_nil
197
+ copy[:b].should be_nil
198
+ end
199
+
200
+ it 'makes the messages state independent' do
201
+ origin = Message.new({:a => 1})
202
+ copy = origin.copy
203
+
204
+ origin.broken!
205
+ copy.consumed!
206
+
207
+ origin.state.should ==(:broken)
208
+ copy.state.should ==(:consumed)
209
+ end
210
+ end
211
+
212
+ context 'generating a correlation id' do
213
+ it 'creates a different CoId for each sequential message' do
214
+ m1, m2, m3 = Message.new, Message.new, Message.new
215
+
216
+ m1.co_id.should_not ==(m2.co_id)
217
+ m1.co_id.should_not ==(m3.co_id)
218
+ m2.co_id.should_not ==(m1.co_id)
219
+ m2.co_id.should_not ==(m3.co_id)
220
+ m3.co_id.should_not ==(m2.co_id)
221
+ m3.co_id.should_not ==(m1.co_id)
222
+ end
223
+
224
+ it 'includes the process id on CoId so that multiple instances have different ids' do
225
+ Message.new.co_id.should match(/#{Process.pid}/)
226
+ end
227
+
228
+ it 'does not change CoId with state chages' do
229
+ m = Message.new
230
+ old_id = m.co_id
231
+
232
+ m.merge!({:some => :thing})
233
+ merged_id = m.co_id
234
+
235
+ m.consumed!
236
+ consumed_id = m.co_id
237
+
238
+ [merged_id, consumed_id].should ==([old_id, old_id])
239
+ end
240
+
241
+ it 'a copied message has a different, yet related, CoId from its origin' do
242
+ origin = Message.new
243
+ copied = origin.copy
244
+ grandcopied = copied.copy
245
+
246
+ origin.co_id.should_not ==(copied.co_id)
247
+ origin.co_id.should_not ==(grandcopied.co_id)
248
+ copied.co_id.should_not ==(grandcopied.co_id)
249
+
250
+ copied.co_id.should match(/#{origin.co_id}/)
251
+ grandcopied.co_id.should match(/#{copied.co_id}/)
252
+ end
253
+ end
135
254
  end
136
255
  end
@@ -1,3 +1,4 @@
1
+ require 'empipelines/message'
1
2
  require 'empipelines/pipeline'
2
3
 
3
4
  def msg(some_map)
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.2
4
+ version: 0.2.3
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-23 00:00:00.000000000Z
13
+ date: 2012-01-02 00:00:00.000000000Z
14
14
  dependencies: []
15
15
  description: Simple Event Handling Pipeline Architecture for EventMachine
16
16
  email: pcalcado+empipelines@gmail.com
@@ -33,10 +33,9 @@ files:
33
33
  - lib/empipelines/aggregated_event_source.rb
34
34
  - lib/empipelines/amqp_event_source.rb
35
35
  - lib/empipelines/batch_event_source.rb
36
- - lib/empipelines/event_handlers.rb
37
36
  - lib/empipelines/event_pipeline.rb
37
+ - lib/empipelines/event_source.rb
38
38
  - lib/empipelines/io_event_source.rb
39
- - lib/empipelines/list_event_source.rb
40
39
  - lib/empipelines/message.rb
41
40
  - lib/empipelines/periodic_event_source.rb
42
41
  - lib/empipelines/pipeline.rb
@@ -45,9 +44,9 @@ files:
45
44
  - unit/empipelines/batch_event_source_spec.rb
46
45
  - unit/empipelines/empty_io_event_source.dat
47
46
  - unit/empipelines/event_pipeline_spec.rb
47
+ - unit/empipelines/event_source_spec.rb
48
48
  - unit/empipelines/io_event_source.dat
49
49
  - unit/empipelines/io_event_source_spec.rb
50
- - unit/empipelines/list_event_source_spec.rb
51
50
  - unit/empipelines/message_spec.rb
52
51
  - unit/empipelines/periodic_event_source_spec.rb
53
52
  - unit/empipelines/pipeline_spec.rb
@@ -68,7 +67,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
67
  version: '0'
69
68
  segments:
70
69
  - 0
71
- hash: -530546752871247334
70
+ hash: 2169333879126328078
72
71
  required_rubygems_version: !ruby/object:Gem::Requirement
73
72
  none: false
74
73
  requirements:
@@ -1,20 +0,0 @@
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
@@ -1,17 +0,0 @@
1
- module EmPipelines
2
- class ListEventSource
3
- def initialize(events)
4
- @events = events
5
- end
6
-
7
- def start!
8
- @events.each do |e|
9
- @handler.call({:payload => e})
10
- end
11
- end
12
-
13
- def on_event(&handler)
14
- @handler = handler
15
- end
16
- end
17
- end
@@ -1,17 +0,0 @@
1
- require 'empipelines/list_event_source'
2
-
3
- module EmPipelines
4
- describe ListEventSource do
5
- it 'sends each element of the list to the handler' do
6
- items = [1, 2, 3, 4, 5, 6]
7
- expected_messages = items.map { |i| {:payload => i} }
8
- received_messages = []
9
-
10
- source = ListEventSource.new(items)
11
- source.on_event { |msg| received_messages << msg}
12
- source.start!
13
-
14
- received_messages.should eql(expected_messages)
15
- end
16
- end
17
- end