active_event 0.5.2 → 0.5.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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +19 -19
- data/README.md +9 -9
- data/app/models/active_event/event.rb +15 -15
- data/app/models/active_event/event_repository.rb +11 -11
- data/db/migrate/00_create_domain_events.rb +9 -9
- data/lib/active_event/autoload.rb +11 -9
- data/lib/active_event/command.rb +35 -39
- data/lib/active_event/domain.rb +4 -2
- data/lib/active_event/event_server.rb +72 -76
- data/lib/active_event/event_source_server.rb +149 -127
- data/lib/active_event/event_type.rb +29 -28
- data/lib/active_event/replay_server.rb +94 -98
- data/lib/active_event/sse.rb +26 -26
- data/lib/active_event/support/attr_initializer.rb +76 -74
- data/lib/active_event/support/attr_setter.rb +31 -29
- data/lib/active_event/support/autoload.rb +46 -44
- data/lib/active_event/support/autoloader.rb +41 -38
- data/lib/active_event/support/multi_logger.rb +31 -28
- data/lib/active_event/validations.rb +68 -68
- data/lib/active_event/validations_registry.rb +18 -18
- data/lib/active_event/version.rb +1 -1
- data/spec/factories/event_factory.rb +9 -9
- data/spec/lib/command_spec.rb +14 -14
- data/spec/lib/domain_spec.rb +21 -21
- data/spec/lib/event_server_spec.rb +29 -29
- data/spec/lib/event_type_spec.rb +38 -38
- data/spec/lib/replay_server_spec.rb +71 -68
- data/spec/lib/support/attr_initializer_spec.rb +55 -55
- data/spec/lib/support/attr_setter_spec.rb +61 -61
- data/spec/models/event_spec.rb +20 -20
- data/spec/spec_helper.rb +1 -1
- data/spec/support/active_record.rb +40 -38
- metadata +2 -4
- data/lib/active_event/support/hash_buffer.rb +0 -24
- data/lib/active_event/support/ring_buffer.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7c3b449c00be517eeb926f40073a2b87c187c43
|
4
|
+
data.tar.gz: 537bb16be762e9dbfd6bfe1b8cc324df759fc4ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28ac276f48f56e6b2262f8d17de64a9f6bd78a22fbdd1f881e51596fc4a7d6f87799d0cfd8ac2078f8d6db8077486e02abc140a72fc9d381719dab7510996659
|
7
|
+
data.tar.gz: 21a962c284462e255635882d3f8fdb0b38147d1d2ed764905a794ea312bf6016a285bdeeeec42e92218a64bce1b4573bb093101027419342dc8ef62c97ee0db4
|
data/MIT-LICENSE
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
Copyright (c) 2013 Andreas Reischuck
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
10
|
-
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
1
|
+
Copyright (c) 2013 Andreas Reischuck
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
# Active Event
|
2
|
-
|
3
|
-
Contains commands, events, validations for Rails Disco.
|
4
|
-
|
5
|
-
Commands are used to transport updates from Rails to the domain.
|
6
|
-
Validations are used to validate commands in Rails and the domain.
|
7
|
-
Events are created in the domain and processed in projections.
|
8
|
-
|
9
|
-
Have a look at the [rails-disco](https://github.com/hicknhack-software/rails-disco/wiki) documentation on Github for more details.
|
1
|
+
# Active Event
|
2
|
+
|
3
|
+
Contains commands, events, validations for Rails Disco.
|
4
|
+
|
5
|
+
Commands are used to transport updates from Rails to the domain.
|
6
|
+
Validations are used to validate commands in Rails and the domain.
|
7
|
+
Events are created in the domain and processed in projections.
|
8
|
+
|
9
|
+
Have a look at the [rails-disco](https://github.com/hicknhack-software/rails-disco/wiki) documentation on Github for more details.
|
@@ -1,15 +1,15 @@
|
|
1
|
-
module ActiveEvent
|
2
|
-
class Event < ActiveRecord::Base
|
3
|
-
self.table_name = 'domain_events'
|
4
|
-
serialize :data, JSON
|
5
|
-
|
6
|
-
# events are only created inside the domain
|
7
|
-
def readonly?
|
8
|
-
persisted?
|
9
|
-
end
|
10
|
-
|
11
|
-
def event_type
|
12
|
-
ActiveEvent::EventType.create_instance(event, data)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
1
|
+
module ActiveEvent
|
2
|
+
class Event < ActiveRecord::Base
|
3
|
+
self.table_name = 'domain_events'
|
4
|
+
serialize :data, JSON
|
5
|
+
|
6
|
+
# events are only created inside the domain
|
7
|
+
def readonly?
|
8
|
+
persisted?
|
9
|
+
end
|
10
|
+
|
11
|
+
def event_type
|
12
|
+
ActiveEvent::EventType.create_instance(event, data)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
module ActiveEvent
|
2
|
-
class EventRepository
|
3
|
-
def self.ordered
|
4
|
-
ActiveEvent::Event.order(:id)
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.after_id(id)
|
8
|
-
ordered.where ActiveEvent::Event.arel_table['id'].gt(id)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
1
|
+
module ActiveEvent
|
2
|
+
class EventRepository
|
3
|
+
def self.ordered
|
4
|
+
ActiveEvent::Event.order(:id)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.after_id(id)
|
8
|
+
ordered.where ActiveEvent::Event.arel_table['id'].gt(id)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
class CreateDomainEvents < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :domain_events do |t|
|
4
|
-
t.string :event
|
5
|
-
t.text :data
|
6
|
-
t.datetime :created_at
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
1
|
+
class CreateDomainEvents < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :domain_events do |t|
|
4
|
+
t.string :event
|
5
|
+
t.text :data
|
6
|
+
t.datetime :created_at
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
module ActiveEvent
|
2
|
-
module Autoload
|
3
|
-
include ActiveEvent::Support::Autoload
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
1
|
+
module ActiveEvent
|
2
|
+
module Autoload
|
3
|
+
include ActiveEvent::Support::Autoload
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def self.dir_names
|
8
|
+
%w(app/commands app/validations app/events)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/active_event/command.rb
CHANGED
@@ -1,39 +1,35 @@
|
|
1
|
-
require 'active_model'
|
2
|
-
|
3
|
-
module ActiveEvent
|
4
|
-
class CommandInvalid < Exception
|
5
|
-
attr_reader :record
|
6
|
-
|
7
|
-
def initialize(record)
|
8
|
-
self.record = record
|
9
|
-
super 'invalid command'
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
attr_writer :record
|
15
|
-
end
|
16
|
-
|
17
|
-
module Command
|
18
|
-
extend ActiveSupport::Concern
|
19
|
-
include ActiveEvent::Support::AttrSetter
|
20
|
-
include ActiveModel::Validations
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module ActiveEvent
|
4
|
+
class CommandInvalid < Exception
|
5
|
+
attr_reader :record
|
6
|
+
|
7
|
+
def initialize(record)
|
8
|
+
self.record = record
|
9
|
+
super 'invalid command'
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_writer :record
|
15
|
+
end
|
16
|
+
|
17
|
+
module Command
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
include ActiveEvent::Support::AttrSetter
|
20
|
+
include ActiveModel::Validations
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def form_name(name)
|
24
|
+
define_singleton_method(:model_name) do
|
25
|
+
@_model_name ||= begin
|
26
|
+
namespace = parents.find do |n|
|
27
|
+
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
|
28
|
+
end
|
29
|
+
ActiveModel::Name.new(self, namespace, name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/active_event/domain.rb
CHANGED
@@ -7,7 +7,9 @@ module ActiveEvent
|
|
7
7
|
|
8
8
|
class DomainException < StandardError
|
9
9
|
# prevent better errors from tracing this exception
|
10
|
-
def __better_errors_bindings_stack
|
10
|
+
def __better_errors_bindings_stack
|
11
|
+
[]
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
module Domain
|
@@ -36,7 +38,7 @@ module ActiveEvent
|
|
36
38
|
|
37
39
|
module ClassMethods
|
38
40
|
def run_command(command)
|
39
|
-
|
41
|
+
instance.run_command command
|
40
42
|
end
|
41
43
|
|
42
44
|
attr_accessor :server_uri
|
@@ -1,76 +1,72 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
require 'bunny'
|
3
|
-
module ActiveEvent
|
4
|
-
class EventServer
|
5
|
-
include Singleton
|
6
|
-
|
7
|
-
def self.publish(event)
|
8
|
-
type = event.class.name
|
9
|
-
body = event.to_json
|
10
|
-
instance.event_exchange.publish body, type: type, headers: event.store_infos
|
11
|
-
LOGGER.debug "Published #{type} with #{body}"
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.start(options)
|
15
|
-
instance.options = options
|
16
|
-
instance.start
|
17
|
-
end
|
18
|
-
|
19
|
-
def start
|
20
|
-
event_connection.start
|
21
|
-
listen_for_resend_requests
|
22
|
-
rescue
|
23
|
-
LOGGER.error e.message
|
24
|
-
LOGGER.error e.backtrace.join("\n")
|
25
|
-
raise e
|
26
|
-
end
|
27
|
-
|
28
|
-
def resend_events_after(id)
|
29
|
-
if @replay_server_thread.nil? || !@replay_server_thread.alive?
|
30
|
-
@replay_server_thread = Thread.new do
|
31
|
-
Thread.current.priority = -10
|
32
|
-
ReplayServer.start options, id
|
33
|
-
end
|
34
|
-
else
|
35
|
-
ReplayServer.update id
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def listen_for_resend_requests
|
40
|
-
resend_request_queue.subscribe do |
|
41
|
-
resend_request_received id
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def resend_request_received
|
46
|
-
LOGGER.debug "received resend request with id #{id}"
|
47
|
-
resend_events_after id.to_i
|
48
|
-
end
|
49
|
-
|
50
|
-
def event_connection
|
51
|
-
@event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
|
52
|
-
end
|
53
|
-
|
54
|
-
def event_channel
|
55
|
-
@event_channel ||= event_connection.create_channel
|
56
|
-
end
|
57
|
-
|
58
|
-
def event_exchange
|
59
|
-
@event_exchange ||= event_channel.fanout options[:event_exchange]
|
60
|
-
end
|
61
|
-
|
62
|
-
def resend_request_exchange
|
63
|
-
@resend_request_exchange ||= event_channel.direct "resend_request_#{options[:event_exchange]}"
|
64
|
-
end
|
65
|
-
|
66
|
-
def resend_request_queue
|
67
|
-
@resend_request_queue ||= event_channel.queue('', auto_delete: true).bind(resend_request_exchange, routing_key: 'resend_request')
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
attr_writer :options
|
75
|
-
end
|
76
|
-
end
|
1
|
+
require 'singleton'
|
2
|
+
require 'bunny'
|
3
|
+
module ActiveEvent
|
4
|
+
class EventServer
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def self.publish(event)
|
8
|
+
type = event.class.name
|
9
|
+
body = event.to_json
|
10
|
+
instance.event_exchange.publish body, type: type, headers: event.store_infos
|
11
|
+
LOGGER.debug "Published #{type} with #{body}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.start(options)
|
15
|
+
instance.options = options
|
16
|
+
instance.start
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
event_connection.start
|
21
|
+
listen_for_resend_requests
|
22
|
+
rescue => e
|
23
|
+
LOGGER.error e.message
|
24
|
+
LOGGER.error e.backtrace.join("\n")
|
25
|
+
raise e
|
26
|
+
end
|
27
|
+
|
28
|
+
def resend_events_after(id)
|
29
|
+
if @replay_server_thread.nil? || !@replay_server_thread.alive?
|
30
|
+
@replay_server_thread = Thread.new do
|
31
|
+
Thread.current.priority = -10
|
32
|
+
ReplayServer.start options, id
|
33
|
+
end
|
34
|
+
else
|
35
|
+
ReplayServer.update id
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def listen_for_resend_requests
|
40
|
+
resend_request_queue.subscribe do |_delivery_info, _properties, id|
|
41
|
+
resend_request_received id
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def resend_request_received(id)
|
46
|
+
LOGGER.debug "received resend request with id #{id}"
|
47
|
+
resend_events_after id.to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def event_connection
|
51
|
+
@event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def event_channel
|
55
|
+
@event_channel ||= event_connection.create_channel
|
56
|
+
end
|
57
|
+
|
58
|
+
def event_exchange
|
59
|
+
@event_exchange ||= event_channel.fanout options[:event_exchange]
|
60
|
+
end
|
61
|
+
|
62
|
+
def resend_request_exchange
|
63
|
+
@resend_request_exchange ||= event_channel.direct "resend_request_#{options[:event_exchange]}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def resend_request_queue
|
67
|
+
@resend_request_queue ||= event_channel.queue('', auto_delete: true).bind(resend_request_exchange, routing_key: 'resend_request')
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_accessor :options
|
71
|
+
end
|
72
|
+
end
|
@@ -1,127 +1,149 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
require 'bunny'
|
3
|
-
|
4
|
-
module ActiveEvent
|
5
|
-
class ProjectionException < StandardError
|
6
|
-
# prevent better errors from tracing this exception
|
7
|
-
def __better_errors_bindings_stack
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
1
|
+
require 'singleton'
|
2
|
+
require 'bunny'
|
3
|
+
|
4
|
+
module ActiveEvent
|
5
|
+
class ProjectionException < StandardError
|
6
|
+
# prevent better errors from tracing this exception
|
7
|
+
def __better_errors_bindings_stack
|
8
|
+
[]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class EventSourceServer
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def wait_for_event_projection(event_id, projection, options = {})
|
17
|
+
instance.wait_for_event_projection(event_id, projection, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def fail_on_projection_error(projection)
|
21
|
+
instance.fail_on_projection_error(projection)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def wait_for_event_projection(event_id, projection, options = {})
|
26
|
+
mutex.synchronize do
|
27
|
+
projection_status = status[projection]
|
28
|
+
projection_status.fail_on_error # projection will not continue if error occurred
|
29
|
+
projection_status.waiter(event_id).wait(mutex, options[:timeout])
|
30
|
+
projection_status.fail_on_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def fail_on_projection_error(projection)
|
35
|
+
mutex.synchronize do
|
36
|
+
projection_status = status[projection]
|
37
|
+
projection_status.fail_on_error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
class Status
|
44
|
+
module UnconditionalVariable
|
45
|
+
def self.wait(_mutex, _timeout = 0)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@event_id = 0
|
51
|
+
@error = nil
|
52
|
+
@backtrace = nil
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@waiters = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def waiter(event)
|
59
|
+
if event > @event_id
|
60
|
+
@waiters[event] ||= ConditionVariable.new
|
61
|
+
else
|
62
|
+
UnconditionalVariable
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_error(error, backtrace)
|
67
|
+
@error, @backtrace = error, backtrace if error || backtrace
|
68
|
+
end
|
69
|
+
|
70
|
+
def event=(event)
|
71
|
+
@event_id = event
|
72
|
+
cvs = []
|
73
|
+
@waiters.delete_if { |event_id, cv| (event_id <= event) && (cvs << cv) }
|
74
|
+
cvs.map &:broadcast
|
75
|
+
end
|
76
|
+
|
77
|
+
def fail_on_error
|
78
|
+
fail ProjectionException, @error, @backtrace if @error
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
attr_accessor :mutex # synchronize access to status
|
83
|
+
attr_accessor :status # status of all projections received so far
|
84
|
+
|
85
|
+
def initialize
|
86
|
+
self.mutex = Mutex.new
|
87
|
+
self.status = Hash.new { |h, k| h[k] = Status.new }
|
88
|
+
event_connection.start
|
89
|
+
event_queue.subscribe do |_delivery_info, _properties, body|
|
90
|
+
process_projection JSON.parse(body).symbolize_keys!
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def process_projection(data)
|
95
|
+
mutex.synchronize do
|
96
|
+
projection_status = status[data[:projection]]
|
97
|
+
projection_status.set_error data[:error], data[:backtrace]
|
98
|
+
projection_status.event = data[:event]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def event_queue
|
103
|
+
@event_queue ||= event_channel.queue('', auto_delete: true).bind(event_exchange)
|
104
|
+
end
|
105
|
+
|
106
|
+
def event_connection
|
107
|
+
@event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
def event_channel
|
111
|
+
@event_channel ||= event_connection.create_channel
|
112
|
+
end
|
113
|
+
|
114
|
+
def event_exchange
|
115
|
+
@@event_exchange ||= event_channel.fanout "server_side_#{options[:event_exchange]}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def default_options
|
119
|
+
{
|
120
|
+
event_connection: {
|
121
|
+
scheme: 'amqp',
|
122
|
+
userinfo: nil,
|
123
|
+
host: '127.0.0.1',
|
124
|
+
port: 9797,
|
125
|
+
},
|
126
|
+
event_exchange: 'events',
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse_options(_args)
|
131
|
+
options = default_options
|
132
|
+
options.merge! YAML.load_file(config_file)[env].deep_symbolize_keys! unless config_file.blank?
|
133
|
+
end
|
134
|
+
|
135
|
+
def config_file
|
136
|
+
File.expand_path('config/disco.yml', Rails.root)
|
137
|
+
end
|
138
|
+
|
139
|
+
def options
|
140
|
+
@options ||= parse_options(ARGV)
|
141
|
+
end
|
142
|
+
|
143
|
+
def env
|
144
|
+
@env = ENV['PROJECTION_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
145
|
+
end
|
146
|
+
|
147
|
+
attr_writer :options
|
148
|
+
end
|
149
|
+
end
|