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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a686659ed279d8bc3e3547139ea2b01c637b8d4
4
- data.tar.gz: c61ec7d584bf564fb12bb5250af1298dfd40efc6
3
+ metadata.gz: f2879ba83c33e9ab5c2a5db8263e01e1cc00f983
4
+ data.tar.gz: d160e8c142951bc87a2a816406ab30bbf6a08b8b
5
5
  SHA512:
6
- metadata.gz: 624483b1eb07a2a56fa84baa1306d1d5a3e591b055afd059efae69f1fcf6df930754dd419cbc74c84ad1fc4efbd8e3968b05bd0d1c0ca58873e8970b79993ec8
7
- data.tar.gz: 0035b8d841c4862928e9022c0ec448fa25cd380aa1b37bd25e19131dee5cea4e83f554e64ddae866c4b64cddefb950d507738a5eff5ae80490abf727a824405c
6
+ metadata.gz: a4f62dfaedef9fe9c2838b9f4035f093a3ac68df9319e6e3e2e9d7b6fbd6f5d6c3db5c83dbbe234e906e338b4e2f4fd56cfc39e7adbc6551b0da0410b972cc1e
7
+ data.tar.gz: 21a407982c30b6bf8140fbb682a47201c45568e5d3ce80a8f052b5106bcb0200ba1f160e7ce032910f488b4beaa9dda2bcedfd3f4ba194b620ac934fc6862709
@@ -1,11 +1,9 @@
1
1
  module ActiveEvent
2
2
  module Autoload
3
- def self.app_path=(path)
4
- dirs = []
5
- dirs << "#{path}/commands/**/*.rb"
6
- dirs << "#{path}/validations/**/*.rb"
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
@@ -20,9 +20,13 @@ module ActiveEvent
20
20
  include ActiveModel::Validations
21
21
 
22
22
  def is_valid_do(&block)
23
- valid = valid?
24
- block.call() if valid
25
- valid
23
+ if valid?
24
+ block.call
25
+ else
26
+ false
27
+ end
28
+ rescue
29
+ false
26
30
  end
27
31
  end
28
32
  end
@@ -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: 'druby', host: 'localhost', port: 8787)
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.start options
15
+ instance.options = options
16
+ instance.start
16
17
  end
17
18
 
18
- def start(options)
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 self.resend_events_after(id)
25
- events = EventRepository.after_id(id).to_a
26
- events.each do |e|
27
- event = EventType.create_instance(e.event, e.data.deep_symbolize_keys)
28
- event.add_store_infos store_id: e.id
29
- self.publish event
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
- event_channel.queue('', auto_delete: true).bind(resend_exchange, routing_key: 'resend').subscribe do |delivery_info, properties, id|
35
- puts "received resend request with id #{id}"
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 resend_exchange
53
- @resend_exchange ||= event_channel.direct "resend_#{options[:event_exchange]}"
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
@@ -21,7 +21,7 @@ module ActiveEvent::Support
21
21
  def attributes(*args)
22
22
  super
23
23
  args.each do |attr|
24
- define_method "#{attr}=", -> (value) { attributes[attr] = value }
24
+ define_method "#{attr}=", lambda { |value| attributes[attr] = value }
25
25
  end
26
26
  end
27
27
  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
@@ -1,7 +1,7 @@
1
1
  module ActiveEvent
2
2
  # Returns the version of the currently loaded ActiveEvent as a Gem::Version
3
3
  def self.version
4
- Gem::Version.new '0.1.0'
4
+ Gem::Version.new '0.2.0'
5
5
  end
6
6
 
7
7
  module VERSION #:nodoc:
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
- it 'resends requests' do
10
- allow(@event).to receive(:event)
11
- allow(@event).to receive(:id)
12
- allow(@event).to receive(:data).and_return({})
13
- expect(ActiveEvent::EventType).to receive(:create_instance).twice.and_return(Object)
14
- expect(Object).to receive(:add_store_infos).twice
15
- expect(ActiveEvent::EventRepository).to receive(:after_id).and_return([@event, @event])
16
- expect(@server.class).to receive(:publish).twice
17
- @server.class.resend_events_after 1
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.1.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-09-11 00:00:00.000000000 Z
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