active_event 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/active_event/autoload.rb +4 -6
- data/lib/active_event/command.rb +7 -3
- data/lib/active_event/domain.rb +1 -1
- data/lib/active_event/event_server.rb +24 -15
- data/lib/active_event/event_source_server.rb +56 -0
- data/lib/active_event/replay_server.rb +94 -0
- data/lib/active_event/support/attr_setter.rb +1 -1
- data/lib/active_event/support/autoload.rb +44 -0
- data/lib/active_event/support/autoloader.rb +29 -0
- data/lib/active_event/version.rb +1 -1
- data/lib/active_event.rb +2 -0
- data/spec/lib/event_server_spec.rb +11 -9
- data/spec/lib/replay_server_spec.rb +66 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2879ba83c33e9ab5c2a5db8263e01e1cc00f983
|
4
|
+
data.tar.gz: d160e8c142951bc87a2a816406ab30bbf6a08b8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4f62dfaedef9fe9c2838b9f4035f093a3ac68df9319e6e3e2e9d7b6fbd6f5d6c3db5c83dbbe234e906e338b4e2f4fd56cfc39e7adbc6551b0da0410b972cc1e
|
7
|
+
data.tar.gz: 21a407982c30b6bf8140fbb682a47201c45568e5d3ce80a8f052b5106bcb0200ba1f160e7ce032910f488b4beaa9dda2bcedfd3f4ba194b620ac934fc6862709
|
@@ -1,11 +1,9 @@
|
|
1
1
|
module ActiveEvent
|
2
2
|
module Autoload
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
dirs << "#{path}/events/**/*.rb"
|
8
|
-
ActiveEvent::Support::Autoloader.load_from dirs
|
3
|
+
include ActiveEvent::Support::Autoload
|
4
|
+
private
|
5
|
+
def self.dir_names
|
6
|
+
%W(app/commands app/validations app/events)
|
9
7
|
end
|
10
8
|
end
|
11
9
|
end
|
data/lib/active_event/command.rb
CHANGED
data/lib/active_event/domain.rb
CHANGED
@@ -34,7 +34,7 @@ module ActiveEvent
|
|
34
34
|
base.server_uri = 'druby://127.0.0.1:8787'
|
35
35
|
end
|
36
36
|
|
37
|
-
def set_config(protocol
|
37
|
+
def set_config(protocol = 'druby', host = 'localhost', port = 8787)
|
38
38
|
self.server_uri = "#{protocol}://#{host}:#{port}"
|
39
39
|
end
|
40
40
|
end
|
@@ -12,31 +12,37 @@ module ActiveEvent
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.start(options)
|
15
|
-
instance.
|
15
|
+
instance.options = options
|
16
|
+
instance.start
|
16
17
|
end
|
17
18
|
|
18
|
-
def start
|
19
|
-
self.options = options
|
19
|
+
def start
|
20
20
|
event_connection.start
|
21
21
|
listen_for_resend_requests
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
def resend_events_after(id)
|
25
|
+
if @replay_server_thread.nil? || !@replay_server_thread.alive?
|
26
|
+
@replay_server_thread = Thread.new do
|
27
|
+
Thread.current.priority = -10
|
28
|
+
ReplayServer.start options, id
|
29
|
+
end
|
30
|
+
else
|
31
|
+
ReplayServer.update id
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def listen_for_resend_requests
|
34
|
-
|
35
|
-
|
36
|
-
self.class.resend_events_after id
|
36
|
+
resend_request_queue.subscribe do |delivery_info, properties, id|
|
37
|
+
resend_request_received id
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
41
|
+
def resend_request_received (id)
|
42
|
+
puts "received resend request with id #{id}"
|
43
|
+
resend_events_after id.to_i
|
44
|
+
end
|
45
|
+
|
40
46
|
def event_connection
|
41
47
|
@event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
|
42
48
|
end
|
@@ -49,15 +55,18 @@ module ActiveEvent
|
|
49
55
|
@event_exchange ||= event_channel.fanout options[:event_exchange]
|
50
56
|
end
|
51
57
|
|
52
|
-
def
|
53
|
-
@
|
58
|
+
def resend_request_exchange
|
59
|
+
@resend_request_exchange ||= event_channel.direct "resend_request_#{options[:event_exchange]}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def resend_request_queue
|
63
|
+
@resend_request_queue ||= event_channel.queue('', auto_delete: true).bind(resend_request_exchange, routing_key: 'resend_request')
|
54
64
|
end
|
55
65
|
|
56
66
|
def options
|
57
67
|
@options
|
58
68
|
end
|
59
69
|
|
60
|
-
private
|
61
70
|
attr_writer :options
|
62
71
|
end
|
63
72
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'reloader/sse'
|
2
|
+
require 'bunny'
|
3
|
+
module ActiveEvent
|
4
|
+
module EventSourceServer
|
5
|
+
def subscribe_to(queue, &block)
|
6
|
+
queue.subscribe(block: true, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def event_queue
|
10
|
+
@event_queue ||= event_channel.queue('', auto_delete: true).bind(event_exchange)
|
11
|
+
end
|
12
|
+
|
13
|
+
def event_connection
|
14
|
+
@event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def event_channel
|
18
|
+
@event_channel ||= event_connection.create_channel
|
19
|
+
end
|
20
|
+
|
21
|
+
def event_exchange
|
22
|
+
@event_exchange ||= event_channel.fanout options[:event_exchange]
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_options
|
26
|
+
{
|
27
|
+
event_connection: {
|
28
|
+
scheme: 'amqp',
|
29
|
+
userinfo: nil,
|
30
|
+
host: '127.0.0.1',
|
31
|
+
port: 9797,
|
32
|
+
},
|
33
|
+
event_exchange: 'events'
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_options(args)
|
38
|
+
options = default_options
|
39
|
+
options.merge! YAML.load_file(config_file)[env].deep_symbolize_keys! unless config_file.blank?
|
40
|
+
end
|
41
|
+
|
42
|
+
def config_file
|
43
|
+
File.expand_path('config/disco.yml', Rails.root)
|
44
|
+
end
|
45
|
+
|
46
|
+
def options
|
47
|
+
@options ||= parse_options(ARGV)
|
48
|
+
end
|
49
|
+
|
50
|
+
def env
|
51
|
+
@env = ENV['PROJECTION_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_writer :options
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'bunny'
|
3
|
+
require 'thread'
|
4
|
+
module ActiveEvent
|
5
|
+
class ReplayServer
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def self.start(options, id)
|
9
|
+
instance.options = options
|
10
|
+
instance.start id
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.update(id)
|
14
|
+
instance.queue << id
|
15
|
+
end
|
16
|
+
|
17
|
+
def start(id)
|
18
|
+
event_connection.start
|
19
|
+
@last_id = id
|
20
|
+
start_republishing
|
21
|
+
send_done_message
|
22
|
+
end
|
23
|
+
|
24
|
+
def queue
|
25
|
+
@queue ||= Queue.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_done_message
|
29
|
+
resend_exchange.publish 'replay_done'
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_republishing
|
33
|
+
loop do
|
34
|
+
@events = EventRepository.after_id(@last_id).to_a
|
35
|
+
return if @events.empty?
|
36
|
+
republish_events
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def republish_events
|
41
|
+
while has_next_event?
|
42
|
+
return if new_id?
|
43
|
+
republish next_event
|
44
|
+
Thread.pass
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def new_id?
|
49
|
+
unless queue.empty?
|
50
|
+
new_id = queue.pop
|
51
|
+
if new_id < @last_id
|
52
|
+
@last_id = new_id
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def republish(e)
|
60
|
+
type = e.event
|
61
|
+
body = e.data.to_json
|
62
|
+
resend_exchange.publish body, {type: type, headers: {store_id: e.id, replayed: true}}
|
63
|
+
puts "Republished #{type} with #{body}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def next_event
|
67
|
+
e = @events.shift
|
68
|
+
@last_id = e.id
|
69
|
+
e
|
70
|
+
end
|
71
|
+
|
72
|
+
def has_next_event?
|
73
|
+
@events.length > 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def event_connection
|
77
|
+
@event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def event_channel
|
81
|
+
@event_channel ||= event_connection.create_channel
|
82
|
+
end
|
83
|
+
|
84
|
+
def resend_exchange
|
85
|
+
@resend_exchange ||= event_channel.fanout "resend_#{options[:event_exchange]}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def options
|
89
|
+
@options
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_writer :options
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ActiveEvent::Support
|
2
|
+
module Autoload
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
module ClassMethods
|
5
|
+
def app_path=(path)
|
6
|
+
set_dirs path
|
7
|
+
Autoloader.load_from dirs
|
8
|
+
end
|
9
|
+
|
10
|
+
def reload_module(module_name)
|
11
|
+
path = [parent.name, module_name.to_s].join('::').underscore
|
12
|
+
Autoloader.reload module_name, path
|
13
|
+
end
|
14
|
+
|
15
|
+
def reload
|
16
|
+
Autoloader.reload_from dirs
|
17
|
+
end
|
18
|
+
|
19
|
+
def watchable_dirs
|
20
|
+
watchable_dirs = {}
|
21
|
+
dir_names.each do |dir_name|
|
22
|
+
watchable_dirs[dir_name] = [:rb]
|
23
|
+
end
|
24
|
+
watchable_dirs
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def dir_names
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_dirs(path)
|
34
|
+
@dirs = dir_names.map do |dir_name|
|
35
|
+
"#{path}/#{dir_name}/**/*.rb"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def dirs
|
40
|
+
@dirs ||= ''
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -5,5 +5,34 @@ module ActiveEvent::Support
|
|
5
5
|
require file
|
6
6
|
end
|
7
7
|
end
|
8
|
+
|
9
|
+
def self.reload_from(dirs)
|
10
|
+
Dir[*dirs].each do |path|
|
11
|
+
reload get_module_name(path), path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.reload(name, path)
|
16
|
+
const_name, namespace_name = name.to_s.split('::').reverse
|
17
|
+
if namespace_name.nil?
|
18
|
+
Object.send(:remove_const, const_name) if Object.const_defined?(const_name)
|
19
|
+
else
|
20
|
+
namespace = const_get(namespace_name)
|
21
|
+
namespace.send(:remove_const, const_name) if namespace.const_defined?(const_name)
|
22
|
+
end
|
23
|
+
$".delete_if { |s| s.include?(path) }
|
24
|
+
require path
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def self.get_module_name(path)
|
29
|
+
segments = path.split('/')
|
30
|
+
seg = if 1 == (segments.length - 2) - (segments.index('app') || segments.index('domain')) #no namespace
|
31
|
+
segments.last.split('.').first
|
32
|
+
else
|
33
|
+
[segments[-2], segments.last.split('.').first].join('/')
|
34
|
+
end
|
35
|
+
seg.camelcase.to_sym
|
36
|
+
end
|
8
37
|
end
|
9
38
|
end
|
data/lib/active_event/version.rb
CHANGED
data/lib/active_event.rb
CHANGED
@@ -10,6 +10,7 @@ module ActiveEvent
|
|
10
10
|
autoload :Domain
|
11
11
|
autoload :EventServer
|
12
12
|
autoload :EventType
|
13
|
+
autoload :ReplayServer
|
13
14
|
autoload :Validations
|
14
15
|
autoload :ValidationsRegistry
|
15
16
|
|
@@ -18,6 +19,7 @@ module ActiveEvent
|
|
18
19
|
|
19
20
|
autoload :AttrInitializer
|
20
21
|
autoload :AttrSetter
|
22
|
+
autoload :Autoload
|
21
23
|
autoload :Autoloader
|
22
24
|
end
|
23
25
|
|
@@ -6,15 +6,17 @@ describe ActiveEvent::EventServer do
|
|
6
6
|
@server = ActiveEvent::EventServer.instance
|
7
7
|
@event = TestEvent.new
|
8
8
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
describe 'resend_events_after' do
|
10
|
+
it 'starts the replay server if its not running' do
|
11
|
+
expect(ActiveEvent::ReplayServer).to receive(:start)
|
12
|
+
@server.resend_events_after(1).join
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'updates the replay server if its running' do
|
16
|
+
@server.instance_variable_set(:@replay_server_thread, Thread.new {sleep 1})
|
17
|
+
expect(ActiveEvent::ReplayServer).to receive(:update)
|
18
|
+
@server.resend_events_after 1
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
it 'publishes an event' do
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
describe ActiveEvent::ReplayServer do
|
3
|
+
class TestEvent
|
4
|
+
end
|
5
|
+
before :all do
|
6
|
+
@server = ActiveEvent::ReplayServer.instance
|
7
|
+
@event = TestEvent.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'republishes an event' do
|
11
|
+
allow(@server).to receive(:resend_exchange).and_return(Object)
|
12
|
+
expect(@server.resend_exchange).to receive(:publish).with("{\"bla\":\"Test2\"}", {:type => "TestEvent", :headers => {store_id: 0, replayed: true}})
|
13
|
+
expect(@event).to receive(:event).and_return(@event.class.name)
|
14
|
+
expect(@event).to receive(:data).and_return(bla: 'Test2')
|
15
|
+
expect(@event).to receive(:id).and_return(0)
|
16
|
+
@server.republish(@event)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'starts and stop republishing events' do
|
20
|
+
expect(ActiveEvent::EventRepository).to receive(:after_id).and_return([1, 2, 3, 4], [])
|
21
|
+
expect(@server).to receive(:republish_events).once
|
22
|
+
@server.start_republishing
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'republish all found events' do
|
26
|
+
@server.instance_variable_set(:@events, [1, 2, 3, 4])
|
27
|
+
expect(Thread).to receive(:pass).exactly(4).times
|
28
|
+
expect(@server).to receive(:next_event).exactly(4).times { @server.instance_variable_get(:@events).shift }
|
29
|
+
expect(@server).to receive(:republish).exactly(4).times
|
30
|
+
@server.republish_events
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'new_id?' do
|
34
|
+
before :each do
|
35
|
+
@server.instance_variable_set(:@last_id, 4)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'overrides id if smaller' do
|
39
|
+
@server.queue << 1
|
40
|
+
expect(@server.new_id?).to be_true
|
41
|
+
expect(@server.instance_variable_get(:@last_id)).to eq 1
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does nothing if id is greater' do
|
45
|
+
@server.queue << 5
|
46
|
+
expect(@server.new_id?).to be_false
|
47
|
+
expect(@server.instance_variable_get(:@last_id)).not_to eq 5
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does nothing if queue is empty' do
|
51
|
+
@server.queue.clear
|
52
|
+
expect(@server.new_id?).to be_false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'can update start id while running' do
|
57
|
+
expect(ActiveEvent::EventRepository).to receive(:after_id).with(1).and_return([2, 3, 4], [])
|
58
|
+
expect(ActiveEvent::EventRepository).to receive(:after_id).with(2).and_return([3, 4])
|
59
|
+
expect(@server).to receive(:next_event).exactly(3).times { @server.instance_variable_get(:@events).shift }
|
60
|
+
expect(@server).to receive(:republish).exactly(3).times
|
61
|
+
expect(Thread).to receive(:pass).exactly(3).times
|
62
|
+
@server.instance_variable_set(:@last_id, 2)
|
63
|
+
@server.queue << 1
|
64
|
+
@server.start_republishing
|
65
|
+
end
|
66
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_event
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Reischuck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -147,9 +147,12 @@ files:
|
|
147
147
|
- lib/active_event/command.rb
|
148
148
|
- lib/active_event/domain.rb
|
149
149
|
- lib/active_event/event_server.rb
|
150
|
+
- lib/active_event/event_source_server.rb
|
150
151
|
- lib/active_event/event_type.rb
|
152
|
+
- lib/active_event/replay_server.rb
|
151
153
|
- lib/active_event/support/attr_initializer.rb
|
152
154
|
- lib/active_event/support/attr_setter.rb
|
155
|
+
- lib/active_event/support/autoload.rb
|
153
156
|
- lib/active_event/support/autoloader.rb
|
154
157
|
- lib/active_event/validations.rb
|
155
158
|
- lib/active_event/validations_registry.rb
|
@@ -162,6 +165,7 @@ files:
|
|
162
165
|
- spec/lib/domain_spec.rb
|
163
166
|
- spec/lib/event_server_spec.rb
|
164
167
|
- spec/lib/event_type_spec.rb
|
168
|
+
- spec/lib/replay_server_spec.rb
|
165
169
|
- spec/lib/support/attr_initializer_spec.rb
|
166
170
|
- spec/lib/support/attr_setter_spec.rb
|
167
171
|
- spec/models/event_spec.rb
|
@@ -197,6 +201,7 @@ test_files:
|
|
197
201
|
- spec/lib/domain_spec.rb
|
198
202
|
- spec/lib/event_server_spec.rb
|
199
203
|
- spec/lib/event_type_spec.rb
|
204
|
+
- spec/lib/replay_server_spec.rb
|
200
205
|
- spec/lib/support/attr_initializer_spec.rb
|
201
206
|
- spec/lib/support/attr_setter_spec.rb
|
202
207
|
- spec/models/event_spec.rb
|