evt-messaging 0.7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/loader.rb +1 -0
- data/lib/messaging.rb +19 -0
- data/lib/messaging/controls.rb +12 -0
- data/lib/messaging/controls/batch.rb +29 -0
- data/lib/messaging/controls/event_data.rb +5 -0
- data/lib/messaging/controls/handler.rb +47 -0
- data/lib/messaging/controls/message.rb +67 -0
- data/lib/messaging/controls/metadata.rb +100 -0
- data/lib/messaging/controls/stream.rb +5 -0
- data/lib/messaging/controls/stream_name.rb +19 -0
- data/lib/messaging/controls/time.rb +5 -0
- data/lib/messaging/controls/write.rb +15 -0
- data/lib/messaging/handle.rb +174 -0
- data/lib/messaging/log.rb +9 -0
- data/lib/messaging/message.rb +82 -0
- data/lib/messaging/message/copy.rb +38 -0
- data/lib/messaging/message/export.rb +9 -0
- data/lib/messaging/message/follow.rb +37 -0
- data/lib/messaging/message/import.rb +9 -0
- data/lib/messaging/message/metadata.rb +58 -0
- data/lib/messaging/message/transformer.rb +54 -0
- data/lib/messaging/message_registry.rb +40 -0
- data/lib/messaging/stream_name.rb +58 -0
- data/lib/messaging/write.rb +138 -0
- data/lib/messaging/write/substitute.rb +64 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9395b1e320f5ac8d62251b28f6bb03d24e2c9d76
|
4
|
+
data.tar.gz: 0afc6dd4852ba62cd022e99f4604a9b84eeeeaed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2561a6d77b386d0ee6728fd970e767cdc2859e8abd456922a2750d8b19f4897ba95c695e48b7e2745ff71f5a7c46f5fa289f5cbc89ea83d57214ed2b970ea0a1
|
7
|
+
data.tar.gz: 3669488d065b94042d98e4945a6ba0865f76da4ac1739c6bbde29a9a15c86ca18a3ed198c386d264c919fa0bd492489c45267c995bf9781979692b5ee25d1e7e
|
data/lib/loader.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
messaging.rb
|
data/lib/messaging.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'event_source'
|
2
|
+
|
3
|
+
require 'messaging/stream_name'
|
4
|
+
|
5
|
+
require 'messaging/message'
|
6
|
+
require 'messaging/message/metadata'
|
7
|
+
require 'messaging/message/copy'
|
8
|
+
require 'messaging/message/follow'
|
9
|
+
|
10
|
+
require 'messaging/message/transformer'
|
11
|
+
require 'messaging/message/import'
|
12
|
+
require 'messaging/message/export'
|
13
|
+
|
14
|
+
require 'messaging/log'
|
15
|
+
require 'messaging/write'
|
16
|
+
require 'messaging/write/substitute'
|
17
|
+
|
18
|
+
require 'messaging/message_registry'
|
19
|
+
require 'messaging/handle'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'clock/controls'
|
2
|
+
require 'event_source/controls'
|
3
|
+
|
4
|
+
require 'messaging/controls/time'
|
5
|
+
require 'messaging/controls/stream'
|
6
|
+
require 'messaging/controls/stream_name'
|
7
|
+
require 'messaging/controls/event_data'
|
8
|
+
require 'messaging/controls/message'
|
9
|
+
require 'messaging/controls/metadata'
|
10
|
+
require 'messaging/controls/batch'
|
11
|
+
require 'messaging/controls/write'
|
12
|
+
require 'messaging/controls/handler'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Controls
|
3
|
+
module Batch
|
4
|
+
def self.example
|
5
|
+
values = [
|
6
|
+
EventSource::Controls::RandomValue.example,
|
7
|
+
EventSource::Controls::RandomValue.example
|
8
|
+
]
|
9
|
+
|
10
|
+
batch = Messages.example
|
11
|
+
|
12
|
+
2.times do |i|
|
13
|
+
batch[i].some_attribute = values[i]
|
14
|
+
end
|
15
|
+
|
16
|
+
return batch, values
|
17
|
+
end
|
18
|
+
|
19
|
+
module Messages
|
20
|
+
def self.example
|
21
|
+
[
|
22
|
+
Controls::Message.example,
|
23
|
+
Controls::Message.example
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Controls
|
3
|
+
module Handler
|
4
|
+
class Example
|
5
|
+
include Messaging::Handle
|
6
|
+
include Controls::Message
|
7
|
+
|
8
|
+
handle SomeMessage do |some_message|
|
9
|
+
some_message.some_attribute = 'some value set by handler'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module HandleMethod
|
14
|
+
class Example
|
15
|
+
include Messaging::Handle
|
16
|
+
|
17
|
+
def handle(event_data)
|
18
|
+
event_data.data = 'some value set by handle method'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module BlockAndHandleMethod
|
24
|
+
class Example
|
25
|
+
include Messaging::Handle
|
26
|
+
include Controls::Message
|
27
|
+
|
28
|
+
handle SomeMessage do |some_message|
|
29
|
+
some_message.some_attribute = 'some attribute value set by handler'
|
30
|
+
end
|
31
|
+
|
32
|
+
def handle(event_data)
|
33
|
+
event_data.data = 'some data value set by handler'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Anomaly
|
39
|
+
module NoHandle
|
40
|
+
class Example
|
41
|
+
include Messaging::Handle
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Controls
|
3
|
+
module Message
|
4
|
+
def self.example(some_attribute: nil, metadata: nil)
|
5
|
+
some_attribute ||= attribute
|
6
|
+
metadata ||= Controls::Metadata.example
|
7
|
+
|
8
|
+
message = SomeMessage.new
|
9
|
+
message.some_attribute = some_attribute
|
10
|
+
message.other_attribute = other_attribute
|
11
|
+
|
12
|
+
message.metadata = metadata
|
13
|
+
|
14
|
+
message
|
15
|
+
end
|
16
|
+
|
17
|
+
module New
|
18
|
+
def self.example
|
19
|
+
Message::SomeMessage.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class SomeMessage
|
24
|
+
include Messaging::Message
|
25
|
+
|
26
|
+
attribute :some_attribute
|
27
|
+
attribute :other_attribute
|
28
|
+
end
|
29
|
+
|
30
|
+
class OtherMessage
|
31
|
+
include Messaging::Message
|
32
|
+
|
33
|
+
attribute :an_attribute
|
34
|
+
attribute :other_attribute
|
35
|
+
end
|
36
|
+
|
37
|
+
class SingleAttribute
|
38
|
+
include Messaging::Message
|
39
|
+
|
40
|
+
attribute :some_attribute
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.message_class
|
44
|
+
SomeMessage
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.type
|
48
|
+
'SomeMessage'
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.attribute
|
52
|
+
'some value'
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.other_attribute
|
56
|
+
'other value'
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.data
|
60
|
+
{
|
61
|
+
some_attribute: attribute,
|
62
|
+
other_attribute: other_attribute
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Controls
|
3
|
+
module Metadata
|
4
|
+
def self.example
|
5
|
+
Messaging::Message::Metadata.build(data)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.source_event_stream_name
|
9
|
+
'someSource'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.source_event_position
|
13
|
+
1
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.causation_event_stream_name
|
17
|
+
"someCausation"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.causation_event_position
|
21
|
+
11
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.correlation_stream_name
|
25
|
+
"someCorrelation"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.reply_stream_name
|
29
|
+
"someReply"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.schema_version
|
33
|
+
11
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.source_event_identifier
|
37
|
+
"#{source_event_stream_name}/#{source_event_position}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.causation_event_identifier
|
41
|
+
"#{causation_event_stream_name}/#{causation_event_position}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.global_position
|
45
|
+
111
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.time
|
49
|
+
Time::Raw.example
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.data
|
53
|
+
{
|
54
|
+
source_event_stream_name: source_event_stream_name,
|
55
|
+
source_event_position: source_event_position,
|
56
|
+
|
57
|
+
causation_event_stream_name: causation_event_stream_name,
|
58
|
+
causation_event_position: causation_event_position,
|
59
|
+
|
60
|
+
correlation_stream_name: correlation_stream_name,
|
61
|
+
|
62
|
+
reply_stream_name: reply_stream_name,
|
63
|
+
|
64
|
+
global_position: global_position,
|
65
|
+
time: time,
|
66
|
+
|
67
|
+
schema_version: schema_version
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
module New
|
72
|
+
def self.example
|
73
|
+
::Messaging::Message::Metadata.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
Empty = New
|
77
|
+
|
78
|
+
module Written
|
79
|
+
def self.example
|
80
|
+
Messaging::Message::Metadata.build(data)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.data
|
84
|
+
data = Metadata.data
|
85
|
+
|
86
|
+
[
|
87
|
+
:source_event_stream_name,
|
88
|
+
:source_event_position,
|
89
|
+
:global_position,
|
90
|
+
:time
|
91
|
+
].each do |not_written_attribute|
|
92
|
+
data.delete(not_written_attribute)
|
93
|
+
end
|
94
|
+
|
95
|
+
data
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Controls
|
3
|
+
StreamName = EventSource::Controls::StreamName
|
4
|
+
|
5
|
+
module StreamName
|
6
|
+
module Named
|
7
|
+
def self.example
|
8
|
+
Example.new
|
9
|
+
end
|
10
|
+
|
11
|
+
class Example
|
12
|
+
include Messaging::StreamName
|
13
|
+
|
14
|
+
category :some_category
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Handle
|
3
|
+
class Error < RuntimeError; end
|
4
|
+
|
5
|
+
def self.included(cls)
|
6
|
+
cls.class_exec do
|
7
|
+
include Log::Dependency
|
8
|
+
|
9
|
+
cls.extend Build
|
10
|
+
cls.extend Call
|
11
|
+
cls.extend Info
|
12
|
+
cls.extend HandleMacro
|
13
|
+
cls.extend MessageRegistry
|
14
|
+
|
15
|
+
virtual :configure
|
16
|
+
|
17
|
+
attr_writer :strict
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def strict
|
22
|
+
@strict ||= false
|
23
|
+
end
|
24
|
+
|
25
|
+
module Build
|
26
|
+
def build(strict: nil)
|
27
|
+
instance = new
|
28
|
+
instance.strict = strict
|
29
|
+
instance.configure
|
30
|
+
instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Call
|
35
|
+
def call(message_or_event_data, strict: nil)
|
36
|
+
instance = build(strict: strict)
|
37
|
+
instance.(message_or_event_data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module Info
|
42
|
+
extend self
|
43
|
+
|
44
|
+
def handler(message_or_event_data)
|
45
|
+
name = handler_name(message_or_event_data)
|
46
|
+
|
47
|
+
if method_defined?(name)
|
48
|
+
return name
|
49
|
+
else
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def handles?(message_or_event_data)
|
55
|
+
method_defined? handler_name(message_or_event_data)
|
56
|
+
end
|
57
|
+
|
58
|
+
def handler_name(message_or_event_data)
|
59
|
+
name = nil
|
60
|
+
|
61
|
+
if message_or_event_data.is_a? EventSource::EventData::Read
|
62
|
+
name = Messaging::Message::Info.canonize_name(message_or_event_data.type)
|
63
|
+
else
|
64
|
+
name = message_or_event_data.message_name
|
65
|
+
end
|
66
|
+
|
67
|
+
"handle_#{name}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module HandleMacro
|
72
|
+
class Error < RuntimeError; end
|
73
|
+
|
74
|
+
def logger
|
75
|
+
@logger ||= Log.get(self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_macro(message_class, &blk)
|
79
|
+
define_handler_method(message_class, &blk)
|
80
|
+
message_registry.register(message_class)
|
81
|
+
end
|
82
|
+
alias :handle :handle_macro
|
83
|
+
|
84
|
+
def define_handler_method(message_class, &blk)
|
85
|
+
handler_method_name = handler_name(message_class)
|
86
|
+
|
87
|
+
if blk.nil?
|
88
|
+
error_msg = "Handler for #{message_class.name} is not correctly defined. It must have a block."
|
89
|
+
logger.error { error_msg }
|
90
|
+
raise Error, error_msg
|
91
|
+
end
|
92
|
+
|
93
|
+
send(:define_method, handler_method_name, &blk)
|
94
|
+
|
95
|
+
handler_method = instance_method(handler_method_name)
|
96
|
+
|
97
|
+
unless handler_method.arity == 1
|
98
|
+
error_msg = "Handler for #{message_class.name} is not correctly defined. It can only have a single parameter."
|
99
|
+
logger.error { error_msg }
|
100
|
+
raise Error, error_msg
|
101
|
+
end
|
102
|
+
|
103
|
+
handler_method_name
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module MessageRegistry
|
108
|
+
def message_registry
|
109
|
+
@message_registry ||= Messaging::MessageRegistry.new
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def call(message_or_event_data)
|
114
|
+
if message_or_event_data.is_a? Message
|
115
|
+
handle_message(message_or_event_data)
|
116
|
+
else
|
117
|
+
handle_event_data(message_or_event_data)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_message(message)
|
122
|
+
logger.trace(tags: [:handle, :message]) { "Handling message (Message class: #{message.class.name})" }
|
123
|
+
logger.trace(tags: [:data, :message, :handle]) { message.pretty_inspect }
|
124
|
+
|
125
|
+
handler = self.class.handler(message)
|
126
|
+
|
127
|
+
unless handler.nil?
|
128
|
+
public_send(handler, message)
|
129
|
+
else
|
130
|
+
if strict
|
131
|
+
error_msg = "#{self.class.name} does not implement a handler for #{message.message_type}. Cannot handle the message."
|
132
|
+
logger.error { error_msg }
|
133
|
+
raise Error, error_msg
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
logger.info(tags: [:handle, :message]) { "Handled message (Message class: #{message.class.name})" }
|
138
|
+
logger.trace(tags: [:data, :message, :handle]) { message.pretty_inspect }
|
139
|
+
|
140
|
+
message
|
141
|
+
end
|
142
|
+
|
143
|
+
def handle_event_data(event_data)
|
144
|
+
logger.trace(tags: [:handle, :event_data]) { "Handling event data (Type: #{event_data.type})" }
|
145
|
+
logger.trace(tags: [:data, :event_data, :handle]) { event_data.pretty_inspect }
|
146
|
+
|
147
|
+
res = nil
|
148
|
+
|
149
|
+
handler = self.class.handler(event_data)
|
150
|
+
|
151
|
+
unless handler.nil?
|
152
|
+
message_name = Messaging::Message::Info.canonize_name(event_data.type)
|
153
|
+
message_class = self.class.message_registry.get(message_name)
|
154
|
+
res = Message::Import.(event_data, message_class)
|
155
|
+
public_send(handler, res)
|
156
|
+
else
|
157
|
+
if respond_to?(:handle)
|
158
|
+
res = handle(event_data)
|
159
|
+
else
|
160
|
+
if strict
|
161
|
+
error_msg = "#{self.class.name} does not implement `handle'. Cannot handle event data."
|
162
|
+
logger.error { error_msg }
|
163
|
+
raise Error, error_msg
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
logger.info(tags: [:handle, :event_data]) { "Handled event data (Type: #{event_data.type})" }
|
169
|
+
logger.info(tags: [:data, :event_data, :handle]) { event_data.pretty_inspect }
|
170
|
+
|
171
|
+
res
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Message
|
3
|
+
def self.included(cls)
|
4
|
+
cls.class_exec do
|
5
|
+
include Schema::DataStructure
|
6
|
+
end
|
7
|
+
|
8
|
+
cls.extend Info
|
9
|
+
cls.extend Build
|
10
|
+
cls.extend Copy
|
11
|
+
cls.extend Follow
|
12
|
+
cls.extend Transformer
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_writer :metadata
|
16
|
+
def metadata
|
17
|
+
@metadata ||= Metadata.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def message_type
|
21
|
+
self.class.message_type
|
22
|
+
end
|
23
|
+
|
24
|
+
def message_name
|
25
|
+
self.class.message_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def follows?(other_message)
|
29
|
+
metadata.follows?(other_message.metadata)
|
30
|
+
end
|
31
|
+
|
32
|
+
module Info
|
33
|
+
extend self
|
34
|
+
|
35
|
+
def message_type(msg=self)
|
36
|
+
class_name(msg).split('::').last
|
37
|
+
end
|
38
|
+
|
39
|
+
def message_name(msg=self)
|
40
|
+
Info.canonize_name(message_type(msg))
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.canonize_name(name)
|
44
|
+
name.gsub(/([^\^])([A-Z])/,'\1_\2').downcase
|
45
|
+
end
|
46
|
+
|
47
|
+
def class_name(message)
|
48
|
+
class_name = nil
|
49
|
+
class_name = message if message.instance_of? String
|
50
|
+
class_name ||= message.name if message.instance_of? Class
|
51
|
+
class_name ||= message.class.name
|
52
|
+
class_name
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Build
|
57
|
+
def build(data=nil, metadata=nil)
|
58
|
+
data ||= {}
|
59
|
+
metadata ||= {}
|
60
|
+
|
61
|
+
metadata = build_metadata(metadata)
|
62
|
+
|
63
|
+
new.tap do |instance|
|
64
|
+
set_attributes(instance, data)
|
65
|
+
instance.metadata = metadata
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_attributes(instance, data)
|
70
|
+
SetAttributes.(instance, data)
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_metadata(metadata)
|
74
|
+
if metadata.nil?
|
75
|
+
Metadata.new
|
76
|
+
else
|
77
|
+
Metadata.build(metadata.to_h)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Message
|
3
|
+
module Copy
|
4
|
+
class Error < RuntimeError; end
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def self.call(source, receiver=nil, copy: nil, include: nil, exclude: nil, metadata: nil, strict: nil)
|
9
|
+
copy(source, receiver, copy: copy, include: include, exclude: exclude, metadata: metadata, strict: strict)
|
10
|
+
end
|
11
|
+
|
12
|
+
def copy(source, receiver=nil, copy: nil, include: nil, exclude: nil, metadata: nil, strict: nil)
|
13
|
+
metadata ||= false
|
14
|
+
strict = true if strict.nil?
|
15
|
+
|
16
|
+
if receiver.nil?
|
17
|
+
receiver = self
|
18
|
+
end
|
19
|
+
|
20
|
+
if receiver.class == Class
|
21
|
+
receiver = receiver.build
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
SetAttributes.(receiver, source, copy: copy, include: include, exclude: exclude, strict: strict)
|
26
|
+
rescue SetAttributes::Attribute::Error => e
|
27
|
+
raise Error, e.message, e.backtrace
|
28
|
+
end
|
29
|
+
|
30
|
+
if metadata
|
31
|
+
SetAttributes.(receiver.metadata, source.metadata)
|
32
|
+
end
|
33
|
+
|
34
|
+
receiver
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Message
|
3
|
+
module Follow
|
4
|
+
class Error < RuntimeError; end
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def self.call(source, receiver=nil, copy: nil, include: nil, exclude: nil, strict: nil)
|
9
|
+
follow(source, receiver, copy: copy, include: include, exclude: exclude, strict: strict)
|
10
|
+
end
|
11
|
+
|
12
|
+
def follow(source, receiver=nil, copy: nil, include: nil, exclude: nil, strict: nil)
|
13
|
+
if receiver.nil?
|
14
|
+
receiver = self
|
15
|
+
end
|
16
|
+
|
17
|
+
if receiver.class == Class
|
18
|
+
receiver = receiver.build
|
19
|
+
end
|
20
|
+
|
21
|
+
unchanged_source_event_stream_name = receiver.metadata.source_event_stream_name
|
22
|
+
unchanged_source_event_position = receiver.metadata.source_event_position
|
23
|
+
unchanged_schema_version = receiver.metadata.schema_version
|
24
|
+
|
25
|
+
Copy.(source, receiver, copy: copy, include: include, exclude: exclude, strict: strict, metadata: true)
|
26
|
+
|
27
|
+
receiver.metadata.source_event_stream_name = unchanged_source_event_stream_name
|
28
|
+
receiver.metadata.source_event_position = unchanged_source_event_position
|
29
|
+
receiver.metadata.schema_version = unchanged_schema_version
|
30
|
+
|
31
|
+
receiver.metadata.follow(source.metadata)
|
32
|
+
|
33
|
+
receiver
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Message
|
3
|
+
class Metadata
|
4
|
+
class Error < RuntimeError; end
|
5
|
+
|
6
|
+
include Schema::DataStructure
|
7
|
+
|
8
|
+
attribute :source_event_stream_name
|
9
|
+
alias :stream_name :source_event_stream_name
|
10
|
+
attribute :source_event_position
|
11
|
+
alias :sequence :source_event_position
|
12
|
+
alias :position :source_event_position
|
13
|
+
alias :position= :source_event_position=
|
14
|
+
|
15
|
+
attribute :causation_event_stream_name
|
16
|
+
attribute :causation_event_position
|
17
|
+
|
18
|
+
attribute :correlation_stream_name
|
19
|
+
|
20
|
+
attribute :reply_stream_name
|
21
|
+
|
22
|
+
attribute :global_position
|
23
|
+
attribute :time
|
24
|
+
|
25
|
+
attribute :schema_version
|
26
|
+
|
27
|
+
def source_event_identifier
|
28
|
+
return nil if source_event_stream_name.nil? || source_event_position.nil?
|
29
|
+
"#{source_event_stream_name}/#{source_event_position}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def causation_event_identifier
|
33
|
+
return nil if causation_event_stream_name.nil? || causation_event_position.nil?
|
34
|
+
"#{causation_event_stream_name}/#{causation_event_position}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def follow(other_metadata)
|
38
|
+
self.causation_event_stream_name = other_metadata.source_event_stream_name
|
39
|
+
self.causation_event_position = other_metadata.source_event_position
|
40
|
+
|
41
|
+
unless follows?(other_metadata)
|
42
|
+
raise Error, "Metadata doesn't have precedence (Metadata: #{self.inspect}, Other Metadata #{other_metadata.inspect})"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def follows?(other_metadata)
|
47
|
+
causation_event_identifier == other_metadata.source_event_identifier &&
|
48
|
+
|
49
|
+
correlation_stream_name == other_metadata.correlation_stream_name &&
|
50
|
+
reply_stream_name == other_metadata.reply_stream_name
|
51
|
+
end
|
52
|
+
|
53
|
+
def clear_reply_stream_name
|
54
|
+
self.reply_stream_name = nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Message
|
3
|
+
module Transformer
|
4
|
+
def self.event_data
|
5
|
+
EventData
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.raw_data(message)
|
9
|
+
message
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.instance(event_data_data, cls)
|
13
|
+
cls.build(event_data_data[:data], event_data_data[:metadata])
|
14
|
+
end
|
15
|
+
|
16
|
+
module EventData
|
17
|
+
def self.write(message)
|
18
|
+
event_data = EventSource::EventData::Write.build
|
19
|
+
|
20
|
+
event_data.type = message.message_type
|
21
|
+
|
22
|
+
event_data.data = message.to_h
|
23
|
+
|
24
|
+
metadata = message.metadata.to_h
|
25
|
+
metadata.delete_if { |k, v| v.nil? || k == :source_event_stream_name || k == :source_event_position}
|
26
|
+
|
27
|
+
event_data.metadata = metadata
|
28
|
+
|
29
|
+
event_data
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.read(event_data)
|
33
|
+
data = event_data.to_h
|
34
|
+
|
35
|
+
unless data[:metadata].nil?
|
36
|
+
data[:metadata] = data[:metadata].clone
|
37
|
+
else
|
38
|
+
data[:metadata] = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
metadata = data[:metadata]
|
42
|
+
|
43
|
+
metadata[:source_event_stream_name] = data[:stream_name]
|
44
|
+
metadata[:source_event_position] = data[:position]
|
45
|
+
|
46
|
+
metadata[:global_position] = data[:global_position]
|
47
|
+
metadata[:time] = data[:time]
|
48
|
+
|
49
|
+
data
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Messaging
|
2
|
+
class MessageRegistry
|
3
|
+
class Error < RuntimeError; end
|
4
|
+
|
5
|
+
include Log::Dependency
|
6
|
+
|
7
|
+
def entries
|
8
|
+
@entries ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(message_name)
|
12
|
+
entries.find do |message_class|
|
13
|
+
message_class.message_name == message_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def register(message_class)
|
18
|
+
logger.trace { "Registering #{message_class}"}
|
19
|
+
if registered?(message_class)
|
20
|
+
error_msg = "#{message_class} is already registered"
|
21
|
+
logger.error { error_msg }
|
22
|
+
raise Error, error_msg
|
23
|
+
end
|
24
|
+
|
25
|
+
entries << message_class
|
26
|
+
|
27
|
+
logger.debug { "Registered #{message_class}"}
|
28
|
+
|
29
|
+
entries
|
30
|
+
end
|
31
|
+
|
32
|
+
def registered?(message_class)
|
33
|
+
entries.include?(message_class)
|
34
|
+
end
|
35
|
+
|
36
|
+
def length
|
37
|
+
entries.length
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Messaging
|
2
|
+
module StreamName
|
3
|
+
extend self
|
4
|
+
|
5
|
+
include EventSource::StreamName
|
6
|
+
|
7
|
+
def self.included(cls)
|
8
|
+
cls.extend Macro
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.activate
|
12
|
+
target_class ||= Object
|
13
|
+
macro_module = Macro
|
14
|
+
return if target_class.is_a? macro_module
|
15
|
+
target_class.extend(macro_module)
|
16
|
+
end
|
17
|
+
|
18
|
+
module Macro
|
19
|
+
def category_macro(category)
|
20
|
+
category = Casing::Camel.(category, symbol_to_string: true)
|
21
|
+
self.send :define_method, :category do
|
22
|
+
@category || category
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias :category :category_macro
|
26
|
+
end
|
27
|
+
|
28
|
+
def stream_name(id, category=nil)
|
29
|
+
category ||= self.category
|
30
|
+
EventSource::StreamName.stream_name category, id
|
31
|
+
end
|
32
|
+
|
33
|
+
def command_stream_name(id, category=nil)
|
34
|
+
category ||= self.category
|
35
|
+
EventSource::StreamName.stream_name "#{category}:command", id
|
36
|
+
end
|
37
|
+
|
38
|
+
def category_stream_name(category=nil)
|
39
|
+
category ||= self.category
|
40
|
+
category
|
41
|
+
end
|
42
|
+
|
43
|
+
def command_category_stream_name(category=nil)
|
44
|
+
category ||= self.category
|
45
|
+
category_stream_name = category_stream_name(category)
|
46
|
+
|
47
|
+
"#{category_stream_name}:command"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.get_category(stream_name)
|
51
|
+
EventSource::StreamName.get_category(stream_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.get_id(stream_name)
|
55
|
+
EventSource::StreamName.get_id stream_name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Write
|
3
|
+
class Error < RuntimeError; end
|
4
|
+
|
5
|
+
def self.included(cls)
|
6
|
+
cls.class_exec do
|
7
|
+
include Log::Dependency
|
8
|
+
|
9
|
+
dependency :event_writer
|
10
|
+
dependency :telemetry, ::Telemetry
|
11
|
+
|
12
|
+
cls.extend Build
|
13
|
+
cls.extend Call
|
14
|
+
cls.extend Configure
|
15
|
+
|
16
|
+
abstract :configure
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Build
|
21
|
+
def build(session: nil)
|
22
|
+
instance = new
|
23
|
+
instance.configure(session: session)
|
24
|
+
::Telemetry.configure instance
|
25
|
+
instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Configure
|
30
|
+
def configure(receiver, session: nil, attr_name: nil)
|
31
|
+
attr_name ||= :write
|
32
|
+
instance = build(session: session)
|
33
|
+
receiver.public_send "#{attr_name}=", instance
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Call
|
38
|
+
def call(message, stream_name, expected_version: nil, reply_stream_name: nil, session: nil)
|
39
|
+
instance = build(session: session)
|
40
|
+
instance.(message, stream_name, expected_version: expected_version, reply_stream_name: reply_stream_name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def call(message_or_batch, stream_name, expected_version: nil, reply_stream_name: nil)
|
45
|
+
unless message_or_batch.is_a? Array
|
46
|
+
logger.trace { "Writing message (Stream Name: #{stream_name}, Type: #{message_or_batch.class.message_type}, Expected Version: #{expected_version.inspect}, Reply Stream Name: #{reply_stream_name.inspect})" }
|
47
|
+
else
|
48
|
+
logger.trace { "Writing batch (Stream Name: #{stream_name}, Expected Version: #{expected_version.inspect}, Reply Stream Name: #{reply_stream_name.inspect})" }
|
49
|
+
end
|
50
|
+
logger.trace(tags: [:data, :message]) { message_or_batch.pretty_inspect }
|
51
|
+
|
52
|
+
message_batch = Array(message_or_batch)
|
53
|
+
|
54
|
+
event_data_batch = event_data_batch(message_batch, reply_stream_name)
|
55
|
+
last_position = event_writer.(event_data_batch, stream_name, expected_version: expected_version)
|
56
|
+
|
57
|
+
unless message_or_batch.is_a? Array
|
58
|
+
logger.info { "Wrote message (Position: #{last_position}, Stream Name: #{stream_name}, Type: #{message_or_batch.class.message_type}, Expected Version: #{expected_version.inspect}, Reply Stream Name: #{reply_stream_name.inspect})" }
|
59
|
+
else
|
60
|
+
logger.info { "Wrote batch (Position: #{last_position}, Stream Name: #{stream_name}, Expected Version: #{expected_version.inspect}, Reply Stream Name: #{reply_stream_name.inspect})" }
|
61
|
+
end
|
62
|
+
logger.info(tags: [:data, :message]) { event_data_batch.pretty_inspect }
|
63
|
+
|
64
|
+
message_batch.each do |message|
|
65
|
+
telemetry.record :written, Telemetry::Data.new(message, stream_name, expected_version, reply_stream_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
last_position
|
69
|
+
end
|
70
|
+
alias :write :call
|
71
|
+
|
72
|
+
def event_data_batch(message_batch, reply_stream_name=nil)
|
73
|
+
event_data_batch = []
|
74
|
+
message_batch.each do |message|
|
75
|
+
unless reply_stream_name.nil?
|
76
|
+
message.metadata.reply_stream_name = reply_stream_name
|
77
|
+
end
|
78
|
+
|
79
|
+
event_data_batch << Message::Export.(message)
|
80
|
+
end
|
81
|
+
|
82
|
+
event_data_batch
|
83
|
+
end
|
84
|
+
|
85
|
+
def reply(message)
|
86
|
+
if message.is_a? Array
|
87
|
+
error_msg = "Cannot reply with a batch"
|
88
|
+
logger.error { error_msg }
|
89
|
+
raise Error, error_msg
|
90
|
+
end
|
91
|
+
|
92
|
+
metadata = message.metadata
|
93
|
+
reply_stream_name = metadata.reply_stream_name
|
94
|
+
|
95
|
+
logger.trace { "Replying (Message Type: #{message.message_type}, Stream Name: #{reply_stream_name.inspect})" }
|
96
|
+
|
97
|
+
if reply_stream_name.nil?
|
98
|
+
error_msg = "Message has no reply stream name. Cannot reply. (Message Type: #{message.message_type})"
|
99
|
+
logger.error { error_msg }
|
100
|
+
logger.error(tags: [:data, :message]) { message.pretty_inspect }
|
101
|
+
raise Error, error_msg
|
102
|
+
end
|
103
|
+
|
104
|
+
metadata.clear_reply_stream_name
|
105
|
+
|
106
|
+
write(message, reply_stream_name).tap do
|
107
|
+
logger.info { "Replied (Message Type: #{message.message_type}, Stream Name: #{reply_stream_name})" }
|
108
|
+
telemetry.record :replied, Telemetry::Data.new(message, reply_stream_name)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def initial(message, stream_name)
|
113
|
+
write(message, stream_name, expected_version: :no_stream)
|
114
|
+
end
|
115
|
+
alias :write_initial :initial
|
116
|
+
|
117
|
+
def self.register_telemetry_sink(writer)
|
118
|
+
sink = Telemetry.sink
|
119
|
+
writer.telemetry.register sink
|
120
|
+
sink
|
121
|
+
end
|
122
|
+
|
123
|
+
module Telemetry
|
124
|
+
class Sink
|
125
|
+
include ::Telemetry::Sink
|
126
|
+
|
127
|
+
record :written
|
128
|
+
record :replied
|
129
|
+
end
|
130
|
+
|
131
|
+
Data = Struct.new :message, :stream_name, :expected_version, :reply_stream_name
|
132
|
+
|
133
|
+
def self.sink
|
134
|
+
Sink.new
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Messaging
|
2
|
+
module Write
|
3
|
+
module Substitute
|
4
|
+
def self.build
|
5
|
+
Substitute::Write.build.tap do |substitute_writer|
|
6
|
+
sink = Messaging::Write.register_telemetry_sink(substitute_writer)
|
7
|
+
substitute_writer.sink = sink
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Write
|
12
|
+
include Messaging::Write
|
13
|
+
|
14
|
+
attr_accessor :sink
|
15
|
+
|
16
|
+
def self.build(session: nil)
|
17
|
+
new.tap do |instance|
|
18
|
+
::Telemetry.configure instance
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def writes(&blk)
|
23
|
+
if blk.nil?
|
24
|
+
return sink.written_records
|
25
|
+
end
|
26
|
+
|
27
|
+
sink.written_records.select do |record|
|
28
|
+
blk.call(record.data.message, record.data.stream_name, record.data.expected_version, record.data.reply_stream_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def written?(&blk)
|
33
|
+
if blk.nil?
|
34
|
+
return sink.recorded_written?
|
35
|
+
end
|
36
|
+
|
37
|
+
sink.recorded_written? do |record|
|
38
|
+
blk.call(record.data.message, record.data.stream_name, record.data.expected_version, record.data.reply_stream_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def replies(&blk)
|
43
|
+
if blk.nil?
|
44
|
+
return sink.replied_records
|
45
|
+
end
|
46
|
+
|
47
|
+
sink.replied_records.select do |record|
|
48
|
+
blk.call(record.data.message, record.data.stream_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def replied?(&blk)
|
53
|
+
if blk.nil?
|
54
|
+
return sink.recorded_replied?
|
55
|
+
end
|
56
|
+
|
57
|
+
sink.recorded_replied? do |record|
|
58
|
+
blk.call(record.data.message, record.data.stream_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: evt-messaging
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- The Eventide Project
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: evt-event_source
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ntl-test_bench
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: " "
|
42
|
+
email: opensource@eventide-project.org
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/loader.rb
|
48
|
+
- lib/messaging.rb
|
49
|
+
- lib/messaging/controls.rb
|
50
|
+
- lib/messaging/controls/batch.rb
|
51
|
+
- lib/messaging/controls/event_data.rb
|
52
|
+
- lib/messaging/controls/handler.rb
|
53
|
+
- lib/messaging/controls/message.rb
|
54
|
+
- lib/messaging/controls/metadata.rb
|
55
|
+
- lib/messaging/controls/stream.rb
|
56
|
+
- lib/messaging/controls/stream_name.rb
|
57
|
+
- lib/messaging/controls/time.rb
|
58
|
+
- lib/messaging/controls/write.rb
|
59
|
+
- lib/messaging/handle.rb
|
60
|
+
- lib/messaging/log.rb
|
61
|
+
- lib/messaging/message.rb
|
62
|
+
- lib/messaging/message/copy.rb
|
63
|
+
- lib/messaging/message/export.rb
|
64
|
+
- lib/messaging/message/follow.rb
|
65
|
+
- lib/messaging/message/import.rb
|
66
|
+
- lib/messaging/message/metadata.rb
|
67
|
+
- lib/messaging/message/transformer.rb
|
68
|
+
- lib/messaging/message_registry.rb
|
69
|
+
- lib/messaging/stream_name.rb
|
70
|
+
- lib/messaging/write.rb
|
71
|
+
- lib/messaging/write/substitute.rb
|
72
|
+
homepage: https://github.com/eventide-project/messaging
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 2.3.3
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.5.2
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Messaging primitives for Eventide
|
96
|
+
test_files: []
|