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 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: []