active_event 0.1.0 → 0.2.0
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.
- 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
|