aggro 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/.travis.yml +15 -0
  4. data/Gemfile +9 -0
  5. data/README.md +5 -1
  6. data/Rakefile +10 -0
  7. data/aggro.gemspec +8 -1
  8. data/lib/aggro.rb +191 -7
  9. data/lib/aggro/abstract_store.rb +12 -0
  10. data/lib/aggro/aggregate.rb +98 -0
  11. data/lib/aggro/aggregate_ref.rb +68 -6
  12. data/lib/aggro/attribute_dsl.rb +96 -0
  13. data/lib/aggro/binding_dsl.rb +45 -0
  14. data/lib/aggro/block_helper.rb +14 -0
  15. data/lib/aggro/channel.rb +37 -0
  16. data/lib/aggro/client.rb +12 -0
  17. data/lib/aggro/cluster_config.rb +57 -0
  18. data/lib/aggro/command.rb +16 -0
  19. data/lib/aggro/concurrent_actor.rb +26 -0
  20. data/lib/aggro/event_bus.rb +94 -0
  21. data/lib/aggro/event_dsl.rb +53 -0
  22. data/lib/aggro/event_proxy.rb +23 -0
  23. data/lib/aggro/event_serializer.rb +14 -0
  24. data/lib/aggro/file_store.rb +97 -0
  25. data/lib/aggro/file_store/reader.rb +21 -0
  26. data/lib/aggro/file_store/writer.rb +27 -0
  27. data/lib/aggro/handler/command.rb +60 -0
  28. data/lib/aggro/handler/create_aggregate.rb +42 -0
  29. data/lib/aggro/handler/get_events.rb +30 -0
  30. data/lib/aggro/handler/query.rb +60 -0
  31. data/lib/aggro/handler/start_saga.rb +56 -0
  32. data/lib/aggro/local_node.rb +28 -0
  33. data/lib/aggro/locator.rb +32 -0
  34. data/lib/aggro/message/ask.rb +16 -0
  35. data/lib/aggro/message/command.rb +36 -0
  36. data/lib/aggro/message/create_aggregate.rb +16 -0
  37. data/lib/aggro/message/endpoint.rb +16 -0
  38. data/lib/aggro/message/events.rb +24 -0
  39. data/lib/aggro/message/get_events.rb +16 -0
  40. data/lib/aggro/message/heartbeat.rb +16 -0
  41. data/lib/aggro/message/invalid_target.rb +20 -0
  42. data/lib/aggro/message/ok.rb +20 -0
  43. data/lib/aggro/message/publisher_endpoint_inquiry.rb +16 -0
  44. data/lib/aggro/message/query.rb +36 -0
  45. data/lib/aggro/message/result.rb +16 -0
  46. data/lib/aggro/message/start_saga.rb +28 -0
  47. data/lib/aggro/message/unhandled_operation.rb +20 -0
  48. data/lib/aggro/message/unknown_operation.rb +20 -0
  49. data/lib/aggro/message_parser.rb +10 -0
  50. data/lib/aggro/message_router.rb +26 -0
  51. data/lib/aggro/nanomsg_transport.rb +31 -0
  52. data/lib/aggro/nanomsg_transport/client.rb +35 -0
  53. data/lib/aggro/nanomsg_transport/connection.rb +98 -0
  54. data/lib/aggro/nanomsg_transport/publish.rb +17 -0
  55. data/lib/aggro/nanomsg_transport/publisher.rb +37 -0
  56. data/lib/aggro/nanomsg_transport/raw_reply.rb +18 -0
  57. data/lib/aggro/nanomsg_transport/raw_request.rb +18 -0
  58. data/lib/aggro/nanomsg_transport/reply.rb +17 -0
  59. data/lib/aggro/nanomsg_transport/request.rb +17 -0
  60. data/lib/aggro/nanomsg_transport/server.rb +84 -0
  61. data/lib/aggro/nanomsg_transport/socket_error.rb +20 -0
  62. data/lib/aggro/nanomsg_transport/subscribe.rb +27 -0
  63. data/lib/aggro/nanomsg_transport/subscriber.rb +82 -0
  64. data/lib/aggro/node.rb +29 -0
  65. data/lib/aggro/node_list.rb +39 -0
  66. data/lib/aggro/projection.rb +13 -0
  67. data/lib/aggro/query.rb +11 -0
  68. data/lib/aggro/saga.rb +94 -0
  69. data/lib/aggro/saga_runner.rb +87 -0
  70. data/lib/aggro/saga_runner/start_saga.rb +12 -0
  71. data/lib/aggro/saga_status.rb +29 -0
  72. data/lib/aggro/server.rb +88 -0
  73. data/lib/aggro/subscriber.rb +48 -0
  74. data/lib/aggro/subscription.rb +41 -0
  75. data/lib/aggro/transform/boolean.rb +16 -0
  76. data/lib/aggro/transform/email.rb +26 -0
  77. data/lib/aggro/transform/id.rb +34 -0
  78. data/lib/aggro/transform/integer.rb +22 -0
  79. data/lib/aggro/transform/money.rb +22 -0
  80. data/lib/aggro/transform/noop.rb +16 -0
  81. data/lib/aggro/transform/string.rb +16 -0
  82. data/lib/aggro/transform/time_interval.rb +24 -0
  83. data/lib/aggro/version.rb +1 -1
  84. data/spec/lib/aggro/abstract_store_spec.rb +15 -0
  85. data/spec/lib/aggro/aggregate_ref_spec.rb +63 -12
  86. data/spec/lib/aggro/aggregate_spec.rb +207 -0
  87. data/spec/lib/aggro/channel_spec.rb +87 -0
  88. data/spec/lib/aggro/client_spec.rb +26 -0
  89. data/spec/lib/aggro/cluster_config_spec.rb +33 -0
  90. data/spec/lib/aggro/command_spec.rb +52 -0
  91. data/spec/lib/aggro/concurrent_actor_spec.rb +44 -0
  92. data/spec/lib/aggro/event_bus_spec.rb +20 -0
  93. data/spec/lib/aggro/event_serializer_spec.rb +28 -0
  94. data/spec/lib/aggro/file_store/reader_spec.rb +32 -0
  95. data/spec/lib/aggro/file_store/writer_spec.rb +67 -0
  96. data/spec/lib/aggro/file_store_spec.rb +51 -0
  97. data/spec/lib/aggro/handler/command_spec.rb +78 -0
  98. data/spec/lib/aggro/handler/create_aggregate_spec.rb +64 -0
  99. data/spec/lib/aggro/handler/get_events_handler_spec.rb +45 -0
  100. data/spec/lib/aggro/handler/query_spec.rb +78 -0
  101. data/spec/lib/aggro/handler/start_saga_spec.rb +64 -0
  102. data/spec/lib/aggro/local_node_spec.rb +52 -0
  103. data/spec/lib/aggro/locator_spec.rb +61 -0
  104. data/spec/lib/aggro/message/ask_spec.rb +23 -0
  105. data/spec/lib/aggro/message/command_spec.rb +50 -0
  106. data/spec/lib/aggro/message/create_aggregate_spec.rb +28 -0
  107. data/spec/lib/aggro/message/endpoint_spec.rb +23 -0
  108. data/spec/lib/aggro/message/events_spec.rb +37 -0
  109. data/spec/lib/aggro/message/get_events_spec.rb +33 -0
  110. data/spec/lib/aggro/message/heartbeat_spec.rb +23 -0
  111. data/spec/lib/aggro/message/invalid_target_spec.rb +28 -0
  112. data/spec/lib/aggro/message/ok_spec.rb +27 -0
  113. data/spec/lib/aggro/message/publisher_endpoint_inquiry_spec.rb +23 -0
  114. data/spec/lib/aggro/message/query_spec.rb +50 -0
  115. data/spec/lib/aggro/message/start_saga_spec.rb +37 -0
  116. data/spec/lib/aggro/message/unhandled_operation_spec.rb +28 -0
  117. data/spec/lib/aggro/message/unknown_operation_spec.rb +28 -0
  118. data/spec/lib/aggro/message_parser_spec.rb +16 -0
  119. data/spec/lib/aggro/message_router_spec.rb +35 -0
  120. data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +21 -0
  121. data/spec/lib/aggro/nanomsg_transport_spec.rb +37 -0
  122. data/spec/lib/aggro/node_list_spec.rb +38 -0
  123. data/spec/lib/aggro/node_spec.rb +44 -0
  124. data/spec/lib/aggro/projection_spec.rb +22 -0
  125. data/spec/lib/aggro/query_spec.rb +47 -0
  126. data/spec/lib/aggro/saga_runner_spec.rb +84 -0
  127. data/spec/lib/aggro/saga_spec.rb +126 -0
  128. data/spec/lib/aggro/saga_status_spec.rb +56 -0
  129. data/spec/lib/aggro/server_spec.rb +118 -0
  130. data/spec/lib/aggro/subscriber_spec.rb +59 -0
  131. data/spec/lib/aggro/subscription_spec.rb +50 -0
  132. data/spec/lib/aggro/transform/boolean_spec.rb +23 -0
  133. data/spec/lib/aggro/transform/email_spec.rb +13 -0
  134. data/spec/lib/aggro/transform/id_spec.rb +70 -0
  135. data/spec/lib/aggro/transform/integer_spec.rb +30 -0
  136. data/spec/lib/aggro/transform/money_spec.rb +34 -0
  137. data/spec/lib/aggro/transform/string_spec.rb +15 -0
  138. data/spec/lib/aggro/transform/time_interval_spec.rb +29 -0
  139. data/spec/lib/aggro_spec.rb +63 -19
  140. data/spec/spec_helper.rb +21 -2
  141. metadata +283 -3
@@ -0,0 +1,32 @@
1
+ module Aggro
2
+ # Public: Locates the nodes responsible for a given entity ID.
3
+ class Locator
4
+ def initialize(id)
5
+ @id = id
6
+ end
7
+
8
+ def local?
9
+ primary_node.is_a? LocalNode
10
+ end
11
+
12
+ def nodes
13
+ current_node_list_state = Aggro.node_list.state
14
+
15
+ if @last_node_list_state == current_node_list_state
16
+ @nodes ||= Aggro.node_list.nodes_for(@id)
17
+ else
18
+ @last_node_list_state = current_node_list_state
19
+
20
+ @nodes = Aggro.node_list.nodes_for(@id)
21
+ end
22
+ end
23
+
24
+ def primary_node
25
+ nodes.first
26
+ end
27
+
28
+ def secondary_nodes
29
+ nodes[1..-1]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Ask message.
4
+ class Ask < Struct.new(:node_id)
5
+ TYPE_CODE = '05'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37]
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{node_id}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Command message.
4
+ class Command < Struct.new(:sender, :commandee_id, :details)
5
+ TYPE_CODE = '03'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37], string[38..73], parse_details(string[74..-1])
9
+ end
10
+
11
+ def self.parse_details(details)
12
+ Marshal.load details
13
+ end
14
+
15
+ def args
16
+ details[:args]
17
+ end
18
+
19
+ def command_class
20
+ ActiveSupport::Inflector.safe_constantize name
21
+ end
22
+
23
+ def name
24
+ details[:name]
25
+ end
26
+
27
+ def to_command
28
+ command_class.new args if command_class
29
+ end
30
+
31
+ def to_s
32
+ "#{TYPE_CODE}#{sender}#{commandee_id}#{Marshal.dump details}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Create aggregate message.
4
+ class CreateAggregate < Struct.new(:sender, :id, :type)
5
+ TYPE_CODE = '08'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37], string[38..73], string[74..-1]
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{sender}#{id}#{type}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Endpoint message.
4
+ class Endpoint < Struct.new(:endpoint)
5
+ TYPE_CODE = '12'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..-1]
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{endpoint}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Get events message.
4
+ class Events < Struct.new(:id, :events)
5
+ TYPE_CODE = '10'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37], parse_events(string[38..-1])
9
+ end
10
+
11
+ def self.parse_events(string)
12
+ ObjectStream.new(StringIO.new(string), type: 'marshal')
13
+ end
14
+
15
+ def serialize_events
16
+ events.map { |event| EventSerializer.serialize event }.join
17
+ end
18
+
19
+ def to_s
20
+ "#{TYPE_CODE}#{id}#{serialize_events}"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Get events message.
4
+ class GetEvents < Struct.new(:sender, :id, :from_version)
5
+ TYPE_CODE = '09'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37], string[38..73], string[74..-1].to_i
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{sender}#{id}#{from_version || 0}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Heartbeat message.
4
+ class Heartbeat < Struct.new(:sender)
5
+ TYPE_CODE = '02'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37]
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{sender}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Invalid target message.
4
+ class InvalidTarget
5
+ TYPE_CODE = '06'.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
@@ -0,0 +1,20 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: OK message.
4
+ class OK
5
+ TYPE_CODE = '01'.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
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Publisher endpoint inquiry message.
4
+ class PublisherEndpointInquiry < Struct.new(:sender)
5
+ TYPE_CODE = '11'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37]
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{sender}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Query message.
4
+ class Query < Struct.new(:sender, :queryable_id, :details)
5
+ TYPE_CODE = '15'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37], string[38..73], parse_details(string[74..-1])
9
+ end
10
+
11
+ def self.parse_details(details)
12
+ Marshal.load details
13
+ end
14
+
15
+ def args
16
+ details[:args]
17
+ end
18
+
19
+ def name
20
+ details[:name]
21
+ end
22
+
23
+ def query_class
24
+ ActiveSupport::Inflector.safe_constantize name
25
+ end
26
+
27
+ def to_query
28
+ query_class.new args if query_class
29
+ end
30
+
31
+ def to_s
32
+ "#{TYPE_CODE}#{sender}#{queryable_id}#{Marshal.dump details}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Result message.
4
+ class Result < Struct.new(:result)
5
+ TYPE_CODE = '14'.freeze
6
+
7
+ def self.parse(string)
8
+ new Marshal.load(string[2..-1])
9
+ end
10
+
11
+ def to_s
12
+ "#{TYPE_CODE}#{Marshal.dump result}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Start saga message.
4
+ class StartSaga < Struct.new(:sender, :id, :details)
5
+ TYPE_CODE = '13'.freeze
6
+
7
+ def self.parse(string)
8
+ new string[2..37], string[38..73], parse_details(string[74..-1])
9
+ end
10
+
11
+ def self.parse_details(details)
12
+ Marshal.load details
13
+ end
14
+
15
+ def args
16
+ details[:args]
17
+ end
18
+
19
+ def name
20
+ details[:name]
21
+ end
22
+
23
+ def to_s
24
+ "#{TYPE_CODE}#{sender}#{id}#{Marshal.dump details}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Unhandled operation message.
4
+ class UnhandledOperation
5
+ TYPE_CODE = '07'.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
@@ -0,0 +1,20 @@
1
+ module Aggro
2
+ module Message
3
+ # Public: Unknown operation message.
4
+ class UnknownOperation
5
+ TYPE_CODE = '04'.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
@@ -0,0 +1,10 @@
1
+ module Aggro
2
+ # Public: Parses inter-node messages.
3
+ module MessageParser
4
+ module_function
5
+
6
+ def parse(message)
7
+ MESSAGE_TYPES[message[0..1]].parse message
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module Aggro
2
+ # Public: Routes inter-node messages to attached message handlers.
3
+ class MessageRouter
4
+ def initialize
5
+ @handlers = {}
6
+ end
7
+
8
+ def attach_handler(message_class, callable = nil, &block)
9
+ if callable
10
+ @handlers[message_class] = callable
11
+ elsif block_given?
12
+ @handlers[message_class] = block
13
+ else
14
+ fail ArgumentError
15
+ end
16
+ end
17
+
18
+ def handles?(message_class)
19
+ @handlers[message_class]
20
+ end
21
+
22
+ def route(message)
23
+ @handlers[message.class].call message if handles?(message.class)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'aggro/nanomsg_transport/client'
2
+ require 'aggro/nanomsg_transport/publisher'
3
+ require 'aggro/nanomsg_transport/server'
4
+ require 'aggro/nanomsg_transport/subscriber'
5
+
6
+ module Aggro
7
+ # Public: Transport layer over nanomsg sockets.
8
+ module NanomsgTransport
9
+ module_function
10
+
11
+ def client(endpoint)
12
+ Client.new endpoint
13
+ end
14
+
15
+ def publisher(endpoint)
16
+ Publisher.new endpoint
17
+ end
18
+
19
+ def server(endpoint, callable = nil, &block)
20
+ Server.new endpoint, callable, &block
21
+ end
22
+
23
+ def subscriber(endpoint, callable = nil, &block)
24
+ Subscriber.new endpoint, callable, &block
25
+ end
26
+
27
+ def teardown
28
+ NNCore::LibNanomsg.nn_term
29
+ end
30
+ end
31
+ end