aggro 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -5
- data/aggro.gemspec +1 -3
- data/lib/aggro.rb +25 -7
- data/lib/aggro/aggregate.rb +33 -6
- data/lib/aggro/attribute_dsl.rb +8 -0
- data/lib/aggro/client.rb +4 -0
- data/lib/aggro/cluster_config.rb +5 -0
- data/lib/aggro/concurrent_actor.rb +1 -1
- data/lib/aggro/event_bus.rb +5 -2
- data/lib/aggro/event_serializer.rb +2 -2
- data/lib/aggro/file_store.rb +2 -2
- data/lib/aggro/file_store/reader.rb +11 -3
- data/lib/aggro/file_store/writer.rb +2 -2
- data/lib/aggro/handler/query.rb +19 -7
- data/lib/aggro/logging.rb +16 -0
- data/lib/aggro/marshal_stream.rb +123 -0
- data/lib/aggro/message/events.rb +6 -2
- data/lib/aggro/message/server_error.rb +20 -0
- data/lib/aggro/node.rb +3 -1
- data/lib/aggro/projection.rb +1 -0
- data/lib/aggro/server.rb +1 -5
- data/lib/aggro/subscription.rb +16 -2
- data/lib/aggro/transform/boolean.rb +14 -2
- data/lib/aggro/transform/date.rb +22 -0
- data/lib/aggro/transform/string.rb +2 -2
- data/lib/aggro/transform/time.rb +22 -0
- data/lib/aggro/version.rb +1 -1
- data/lib/aggro/zeromq_transport.rb +44 -0
- data/lib/aggro/{nanomsg_transport → zeromq_transport}/client.rb +11 -9
- data/lib/aggro/{nanomsg_transport → zeromq_transport}/publisher.rb +11 -8
- data/lib/aggro/zeromq_transport/server.rb +92 -0
- data/lib/aggro/{nanomsg_transport → zeromq_transport}/subscriber.rb +23 -24
- data/spec/lib/aggro/event_serializer_spec.rb +1 -1
- data/spec/lib/aggro/file_store/reader_spec.rb +2 -1
- data/spec/lib/aggro/file_store/writer_spec.rb +10 -7
- data/spec/lib/aggro/local_node_spec.rb +2 -2
- data/spec/lib/aggro/marshal_stream_spec.rb +17 -0
- data/spec/lib/aggro/message/events_spec.rb +4 -3
- data/spec/lib/aggro/node_spec.rb +3 -3
- data/spec/lib/aggro/subscription_spec.rb +4 -2
- data/spec/lib/aggro/{nanomsg_transport_spec.rb → zeromq_transport_spec.rb} +11 -7
- data/spec/spec_helper.rb +8 -1
- metadata +17 -50
- data/lib/aggro/abstract_store.rb +0 -12
- data/lib/aggro/nanomsg_transport/connection.rb +0 -98
- data/lib/aggro/nanomsg_transport/publish.rb +0 -17
- data/lib/aggro/nanomsg_transport/raw_reply.rb +0 -18
- data/lib/aggro/nanomsg_transport/raw_request.rb +0 -18
- data/lib/aggro/nanomsg_transport/reply.rb +0 -17
- data/lib/aggro/nanomsg_transport/request.rb +0 -17
- data/lib/aggro/nanomsg_transport/server.rb +0 -84
- data/lib/aggro/nanomsg_transport/socket_error.rb +0 -20
- data/lib/aggro/nanomsg_transport/subscribe.rb +0 -27
- data/spec/lib/aggro/abstract_store_spec.rb +0 -15
- data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +0 -21
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
# Private: Mixin for logging concerns.
|
5
|
+
module Logging
|
6
|
+
include Logger::Severity
|
7
|
+
|
8
|
+
def log(level, progname, message = nil, &block)
|
9
|
+
(@logger || Aggro.logger).call level, progname, message, &block
|
10
|
+
rescue => e
|
11
|
+
$stderr.puts '`Aggro.logger` failed to log ' \
|
12
|
+
"#{[level, progname, message, block].join(' ')}\n" \
|
13
|
+
"#{e.message} (#{e.class})\n#{e.backtrace.join "\n"}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Private: Wrapper around an IO object to read/write Marshaled objects.
|
2
|
+
class MarshalStream
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
DEFAULT_MAX_OUTBOX = 10
|
6
|
+
|
7
|
+
class StreamError < StandardError; end
|
8
|
+
|
9
|
+
attr_reader :io
|
10
|
+
attr_reader :max_outbox
|
11
|
+
|
12
|
+
def initialize(io, max_outbox: DEFAULT_MAX_OUTBOX)
|
13
|
+
@io = io
|
14
|
+
@max_outbox = max_outbox
|
15
|
+
@inbox = []
|
16
|
+
@outbox = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def close
|
20
|
+
flush_outbox
|
21
|
+
io.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def closed?
|
25
|
+
io.closed?
|
26
|
+
end
|
27
|
+
|
28
|
+
def each
|
29
|
+
return to_enum unless block_given?
|
30
|
+
|
31
|
+
read { |obj| yield obj } until eof
|
32
|
+
end
|
33
|
+
|
34
|
+
def eof?
|
35
|
+
inbox.empty? && io.eof?
|
36
|
+
end
|
37
|
+
|
38
|
+
alias_method :eof, :eof?
|
39
|
+
|
40
|
+
def flush_buffer
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def flush_outbox
|
45
|
+
outbox.each { |obj| write_to_stream(obj.is_a? Proc ? obj.call : obj) }
|
46
|
+
outbox.clear
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def read
|
52
|
+
if block_given?
|
53
|
+
read_from_inbox { |obj| yield obj }
|
54
|
+
read_from_stream { |obj| yield obj }
|
55
|
+
|
56
|
+
nil
|
57
|
+
else
|
58
|
+
read_one
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def read_from_stream
|
63
|
+
yield Marshal.load(io)
|
64
|
+
rescue IOError, SystemCallError
|
65
|
+
raise
|
66
|
+
rescue => e
|
67
|
+
raise StreamError, "Unreadble stream: #{e}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def read_one
|
71
|
+
return inbox.shift unless inbox.empty?
|
72
|
+
|
73
|
+
result = nil
|
74
|
+
|
75
|
+
read { |obj| result.nil? ? result = obj : (inbox << obj) } while result.nil?
|
76
|
+
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_io
|
81
|
+
io
|
82
|
+
end
|
83
|
+
|
84
|
+
def write(*objects)
|
85
|
+
write_to_buffer(*objects)
|
86
|
+
flush_buffer
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :<<, :write
|
90
|
+
|
91
|
+
def write_to_buffer(*objects)
|
92
|
+
flush_outbox
|
93
|
+
objects.each { |object| write_to_stream object }
|
94
|
+
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_to_outbox(object = nil, &block)
|
99
|
+
outbox << (block || object)
|
100
|
+
|
101
|
+
flush_outbox if outbox.size > max_outbox
|
102
|
+
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def write_to_stream(object)
|
107
|
+
Marshal.dump(object, io)
|
108
|
+
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
attr_reader :inbox
|
115
|
+
attr_reader :outbox
|
116
|
+
|
117
|
+
def read_from_inbox
|
118
|
+
return if inbox.empty?
|
119
|
+
|
120
|
+
inbox.each { |obj| yield obj }
|
121
|
+
inbox.clear
|
122
|
+
end
|
123
|
+
end
|
data/lib/aggro/message/events.rb
CHANGED
@@ -9,11 +9,15 @@ module Aggro
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.parse_events(string)
|
12
|
-
|
12
|
+
Enumerator.new do |yielder|
|
13
|
+
MarshalStream.new(StringIO.new(string)).each do |raw_event|
|
14
|
+
yielder << EventSerializer.deserialize(raw_event)
|
15
|
+
end
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
def serialize_events
|
16
|
-
events.map { |event| EventSerializer.serialize event }.join
|
20
|
+
events.map { |event| Marshal.dump EventSerializer.serialize event }.join
|
17
21
|
end
|
18
22
|
|
19
23
|
def to_s
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Message
|
3
|
+
# Public: OK message.
|
4
|
+
class ServerError
|
5
|
+
TYPE_CODE = '00'.freeze
|
6
|
+
|
7
|
+
def self.parse(_string)
|
8
|
+
new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.new
|
12
|
+
@singleton ||= super
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{TYPE_CODE}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/aggro/node.rb
CHANGED
@@ -20,7 +20,9 @@ module Aggro
|
|
20
20
|
response = client.post(message)
|
21
21
|
|
22
22
|
if response.is_a? Message::Endpoint
|
23
|
-
response.endpoint
|
23
|
+
port = URI.parse(response.endpoint).port
|
24
|
+
|
25
|
+
URI.parse(endpoint).tap { |uri| uri.port = port }.to_s
|
24
26
|
else
|
25
27
|
fail "Could not discover publisher endpoint for #{id}"
|
26
28
|
end
|
data/lib/aggro/projection.rb
CHANGED
data/lib/aggro/server.rb
CHANGED
@@ -35,8 +35,8 @@ module Aggro
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def stop
|
38
|
-
@transport_server.stop
|
39
38
|
@transport_publisher.close_socket
|
39
|
+
@transport_server.stop
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
@@ -80,9 +80,5 @@ module Aggro
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
84
|
-
def publisher
|
85
|
-
@publisher ||= Publisher.new(local_node.publisher_endpoint)
|
86
|
-
end
|
87
83
|
end
|
88
84
|
end
|
data/lib/aggro/subscription.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Aggro
|
2
2
|
# Private: Handles invoking events on a subscriber object.
|
3
3
|
class Subscription
|
4
|
+
attr_reader :caught_up
|
5
|
+
|
4
6
|
def initialize(topic, subscriber, namespace, filters, at_version)
|
5
7
|
@topic = topic
|
6
8
|
@subscriber = subscriber
|
@@ -21,15 +23,27 @@ module Aggro
|
|
21
23
|
invoke(event) if handles_event?(event) && matches_filter?(event)
|
22
24
|
end
|
23
25
|
|
26
|
+
def notify_subscription_caught_up
|
27
|
+
@caught_up = true
|
28
|
+
|
29
|
+
return unless @subscriber.handles_event? :caught_up, @namespace
|
30
|
+
|
31
|
+
@subscriber.send "#{@namespace}_caught_up"
|
32
|
+
end
|
33
|
+
|
24
34
|
private
|
25
35
|
|
36
|
+
def expand_event(event)
|
37
|
+
event.details.merge now: event.occured_at, today: event.occured_at.to_date
|
38
|
+
end
|
39
|
+
|
26
40
|
def handles_event?(event)
|
27
41
|
@subscriber.handles_event? event.name, @namespace
|
28
42
|
end
|
29
43
|
|
30
44
|
def invoke(event)
|
31
|
-
Invokr.invoke method: "#{@namespace}_#{event.name}",
|
32
|
-
using: event
|
45
|
+
Invokr.invoke method: "#{@namespace}_#{event.name}", on: @subscriber,
|
46
|
+
using: expand_event(event)
|
33
47
|
end
|
34
48
|
|
35
49
|
def matches_filter?(event)
|
@@ -5,12 +5,24 @@ module Aggro
|
|
5
5
|
module_function
|
6
6
|
|
7
7
|
def deserialize(value)
|
8
|
-
value if value
|
8
|
+
value if truthy?(value) || falsey?(value)
|
9
9
|
end
|
10
10
|
|
11
11
|
def serialize(value)
|
12
|
-
value if value
|
12
|
+
value if truthy?(value) || falsey?(value)
|
13
13
|
end
|
14
|
+
|
15
|
+
def falsey?(value)
|
16
|
+
value == false || value == 'false' || value == '0'
|
17
|
+
end
|
18
|
+
|
19
|
+
private :falsey?
|
20
|
+
|
21
|
+
def truthy?(value)
|
22
|
+
value == true || value == 'true' || value == '1'
|
23
|
+
end
|
24
|
+
|
25
|
+
private :truthy?
|
14
26
|
end
|
15
27
|
end
|
16
28
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms date representations.
|
4
|
+
module Date
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def deserialize(value)
|
8
|
+
if value.is_a? ::String
|
9
|
+
::Date.parse(value)
|
10
|
+
elsif value.is_a? ::Integer
|
11
|
+
::Date.parse(value.to_s)
|
12
|
+
elsif value.is_a? ::Date
|
13
|
+
value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(value)
|
18
|
+
value.to_s if value.is_a? ::Date
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms time representations.
|
4
|
+
module Time
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def deserialize(value)
|
8
|
+
if value.is_a? ::String
|
9
|
+
::Time.parse(value)
|
10
|
+
elsif value.is_a? ::Integer
|
11
|
+
::Time.parse(value.to_s)
|
12
|
+
elsif value.is_a? ::Time
|
13
|
+
value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(value)
|
18
|
+
value.to_s if value.is_a? ::Time
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/aggro/version.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'aggro/zeromq_transport/client'
|
2
|
+
require 'aggro/zeromq_transport/publisher'
|
3
|
+
require 'aggro/zeromq_transport/server'
|
4
|
+
require 'aggro/zeromq_transport/subscriber'
|
5
|
+
|
6
|
+
module Aggro
|
7
|
+
# Public: Transport layer over nanomsg sockets.
|
8
|
+
module ZeroMQTransport
|
9
|
+
class << self
|
10
|
+
attr_writer :linger
|
11
|
+
end
|
12
|
+
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def client(endpoint)
|
16
|
+
Client.new endpoint
|
17
|
+
end
|
18
|
+
|
19
|
+
def context
|
20
|
+
@context ||= ZeroMQ::Context.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def linger
|
24
|
+
@linger ||= 1_000
|
25
|
+
end
|
26
|
+
|
27
|
+
def publisher(endpoint)
|
28
|
+
Publisher.new endpoint
|
29
|
+
end
|
30
|
+
|
31
|
+
def server(endpoint, callable = nil, &block)
|
32
|
+
Server.new endpoint, callable, &block
|
33
|
+
end
|
34
|
+
|
35
|
+
def subscriber(endpoint, callable = nil, &block)
|
36
|
+
Subscriber.new endpoint, callable, &block
|
37
|
+
end
|
38
|
+
|
39
|
+
def teardown
|
40
|
+
@context.terminate if @context
|
41
|
+
@context = nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,23 +1,22 @@
|
|
1
|
-
require 'aggro/nanomsg_transport/request'
|
2
|
-
|
3
1
|
module Aggro
|
4
|
-
module
|
2
|
+
module ZeroMQTransport
|
5
3
|
# Public: Client for making requests against a nanomsg server.
|
6
4
|
class Client
|
7
5
|
def initialize(endpoint)
|
8
|
-
ObjectSpace.define_finalizer self, method(:close_socket)
|
9
|
-
|
10
6
|
@endpoint = endpoint
|
11
7
|
end
|
12
8
|
|
13
9
|
def post(message)
|
14
|
-
request_socket.
|
10
|
+
request_socket.send_string message.to_s
|
15
11
|
|
16
|
-
|
12
|
+
response = ''
|
13
|
+
request_socket.recv_string response
|
14
|
+
|
15
|
+
response
|
17
16
|
end
|
18
17
|
|
19
18
|
def close_socket
|
20
|
-
request_socket.
|
19
|
+
request_socket.close if @open
|
21
20
|
@request_socket = nil
|
22
21
|
@open = false
|
23
22
|
end
|
@@ -27,7 +26,10 @@ module Aggro
|
|
27
26
|
def request_socket
|
28
27
|
@request_socket ||= begin
|
29
28
|
@open = true
|
30
|
-
|
29
|
+
socket = ZeroMQTransport.context.socket(ZMQ::REQ)
|
30
|
+
socket.connect @endpoint
|
31
|
+
|
32
|
+
socket
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
@@ -1,17 +1,15 @@
|
|
1
|
-
require 'aggro/nanomsg_transport/publish'
|
2
|
-
|
3
1
|
module Aggro
|
4
|
-
module
|
2
|
+
module ZeroMQTransport
|
5
3
|
# Public: Handles publishing messages on a given endpoint.
|
6
4
|
class Publisher
|
7
5
|
def initialize(endpoint)
|
8
|
-
ObjectSpace.define_finalizer self, method(:close_socket)
|
9
|
-
|
10
6
|
@endpoint = endpoint
|
11
7
|
end
|
12
8
|
|
13
9
|
def close_socket
|
14
|
-
|
10
|
+
return unless @open && @pub_socket
|
11
|
+
|
12
|
+
@pub_socket.close if @pub_socket
|
15
13
|
@pub_socket = nil
|
16
14
|
@open = false
|
17
15
|
end
|
@@ -20,11 +18,16 @@ module Aggro
|
|
20
18
|
return @pub_socket if @open
|
21
19
|
|
22
20
|
@open = true
|
23
|
-
|
21
|
+
|
22
|
+
@pub_socket = ZeroMQTransport.context.socket(ZMQ::PUB)
|
23
|
+
@pub_socket.setsockopt ZMQ::LINGER, 1_000
|
24
|
+
@pub_socket.bind @endpoint
|
25
|
+
|
26
|
+
@pub_socket
|
24
27
|
end
|
25
28
|
|
26
29
|
def publish(message)
|
27
|
-
pub_socket.
|
30
|
+
pub_socket.send_string message.to_s
|
28
31
|
end
|
29
32
|
|
30
33
|
private
|