evt-messaging 0.7.1.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 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,5 @@
1
+ module Messaging
2
+ module Controls
3
+ EventData = EventSource::Controls::EventData
4
+ end
5
+ 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,5 @@
1
+ module Messaging
2
+ module Controls
3
+ Stream = EventSource::Controls::Stream
4
+ end
5
+ 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,5 @@
1
+ module Messaging
2
+ module Controls
3
+ Time = Clock::Controls::Time
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module Messaging
2
+ module Controls
3
+ module Write
4
+ def self.example
5
+ Example.build
6
+ end
7
+
8
+ class Example
9
+ include Messaging::Write
10
+
11
+ virtual :configure
12
+ end
13
+ end
14
+ end
15
+ 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,9 @@
1
+ module Messaging
2
+ class Log < ::Log
3
+ def tag!(tags)
4
+ tags << :messaging
5
+ tags << :library
6
+ tags << :verbose
7
+ end
8
+ end
9
+ 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,9 @@
1
+ module Messaging
2
+ module Message
3
+ module Export
4
+ def self.call(event_data)
5
+ Transform::Write.(event_data, :event_data)
6
+ end
7
+ end
8
+ end
9
+ 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,9 @@
1
+ module Messaging
2
+ module Message
3
+ module Import
4
+ def self.call(event_data, message_class)
5
+ Transform::Read.(event_data, :event_data, message_class)
6
+ end
7
+ end
8
+ end
9
+ 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: []