synapse-core 0.1.2 → 0.2.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.
Files changed (57) hide show
  1. data/lib/synapse/auditing/audit_logger.rb +28 -0
  2. data/lib/synapse/auditing/data_provider.rb +42 -0
  3. data/lib/synapse/auditing/dispatch_interceptor.rb +29 -0
  4. data/lib/synapse/auditing/unit_listener.rb +53 -0
  5. data/lib/synapse/auditing.rb +4 -0
  6. data/lib/synapse/command.rb +34 -0
  7. data/lib/synapse/{duplication.rb → common/duplication.rb} +0 -0
  8. data/lib/synapse/{errors.rb → common/errors.rb} +0 -0
  9. data/lib/synapse/{identifier.rb → common/identifier.rb} +2 -1
  10. data/lib/synapse/{message.rb → common/message.rb} +0 -0
  11. data/lib/synapse/{message_builder.rb → common/message_builder.rb} +0 -0
  12. data/lib/synapse/domain.rb +5 -0
  13. data/lib/synapse/event_bus.rb +5 -0
  14. data/lib/synapse/event_sourcing/conflict_resolver.rb +4 -6
  15. data/lib/synapse/event_sourcing/member.rb +11 -2
  16. data/lib/synapse/event_sourcing/snapshot/taker.rb +0 -18
  17. data/lib/synapse/event_sourcing.rb +13 -0
  18. data/lib/synapse/event_store/mongo/cursor_event_stream.rb +3 -3
  19. data/lib/synapse/event_store/mongo.rb +8 -0
  20. data/lib/synapse/event_store.rb +11 -0
  21. data/lib/synapse/partitioning/amqp/amqp_queue_reader.rb +50 -0
  22. data/lib/synapse/partitioning/amqp/amqp_queue_writer.rb +31 -0
  23. data/lib/synapse/partitioning/amqp/key_resolver.rb +26 -0
  24. data/lib/synapse/partitioning/amqp.rb +3 -0
  25. data/lib/synapse/partitioning/memory_queue_reader.rb +31 -0
  26. data/lib/synapse/partitioning/memory_queue_writer.rb +19 -0
  27. data/lib/synapse/partitioning/message_receipt.rb +25 -0
  28. data/lib/synapse/partitioning/packing/json_packer.rb +93 -0
  29. data/lib/synapse/partitioning/packing/json_unpacker.rb +83 -0
  30. data/lib/synapse/partitioning/packing.rb +27 -0
  31. data/lib/synapse/partitioning/queue_reader.rb +32 -0
  32. data/lib/synapse/partitioning/queue_writer.rb +17 -0
  33. data/lib/synapse/partitioning.rb +20 -0
  34. data/lib/synapse/process_manager.rb +4 -0
  35. data/lib/synapse/repository/locking.rb +3 -8
  36. data/lib/synapse/repository.rb +7 -0
  37. data/lib/synapse/serialization/converter/bson.rb +28 -0
  38. data/lib/synapse/serialization/serializer/attribute.rb +48 -0
  39. data/lib/synapse/serialization/serializer.rb +1 -1
  40. data/lib/synapse/serialization.rb +46 -0
  41. data/lib/synapse/uow/factory.rb +5 -5
  42. data/lib/synapse/uow/uow.rb +13 -13
  43. data/lib/synapse/uow.rb +8 -0
  44. data/lib/synapse/upcasting/chain.rb +1 -1
  45. data/lib/synapse/upcasting.rb +5 -0
  46. data/lib/synapse/version.rb +1 -1
  47. data/lib/synapse/wiring.rb +3 -0
  48. data/lib/synapse.rb +26 -338
  49. data/test/auditing/data_provider_test.rb +30 -0
  50. data/test/auditing/dispatch_interceptor_test.rb +25 -0
  51. data/test/auditing/unit_listener_test.rb +70 -0
  52. data/test/partitioning/memory_test.rb +34 -0
  53. data/test/partitioning/packing/json_test.rb +61 -0
  54. data/test/test_ext.rb +14 -0
  55. data/test/test_helper.rb +4 -0
  56. metadata +45 -24
  57. data/test/event_sourcing/snapshot/deferred_taker_test.rb +0 -19
@@ -0,0 +1,83 @@
1
+ module Synapse
2
+ module Partitioning
3
+ # Implementation of a message unpacker that unpacks messages that come off the wire in the
4
+ # format described by JsonMessagePacker
5
+ class JsonMessageUnpacker < MessageUnpacker
6
+ # @param [Serializer] serializer
7
+ # @return [undefined]
8
+ def initialize(serializer)
9
+ @serializer = serializer
10
+ end
11
+
12
+ # @param [String] packed
13
+ # @return [Message]
14
+ def unpack_message(message)
15
+ packed = JSON.load message
16
+ packed.symbolize_keys!
17
+
18
+ message_type = packed.fetch(:message_type).to_sym
19
+ builder = builder_for(message_type).new
20
+
21
+ builder.id = packed.fetch :id
22
+ builder.metadata = deserialize_metadata packed
23
+ builder.payload = deserialize_payload packed
24
+
25
+ if [:event, :domain_event].include? message_type
26
+ timestamp = packed.fetch :timestamp
27
+ builder.timestamp = Time.at timestamp
28
+ end
29
+
30
+ if :domain_event == message_type
31
+ builder.aggregate_id = packed.fetch :aggregate_id
32
+ builder.sequence_number = packed.fetch :sequence_number
33
+ end
34
+
35
+ builder.build
36
+ end
37
+
38
+ private
39
+
40
+ # Deserializes the metadata of the given packed message
41
+ #
42
+ # @param [Hash] packed
43
+ # @return [Hash]
44
+ def deserialize_metadata(packed)
45
+ content = packed.fetch :metadata
46
+ @serializer.deserialize Serialization::SerializedMetadata.new content, content.class
47
+ end
48
+
49
+ # Deserializes the payload of the given packed message
50
+ #
51
+ # @param [Hash] packed
52
+ # @return [Object]
53
+ def deserialize_payload(packed)
54
+ content = packed.fetch :payload
55
+ name = packed.fetch :payload_type
56
+ revision = packed.fetch :payload_revision
57
+
58
+ serialized_type = Serialization::SerializedType.new name, revision
59
+ serialized_object = Serialization::SerializedObject.new content, content.class, serialized_type
60
+
61
+ @serializer.deserialize serialized_object
62
+ end
63
+
64
+ # Returns the builder type for the given message type
65
+ #
66
+ # @raise [ArgumentError] If message type isn't supported by this unpacker
67
+ # @param [Symbol] type
68
+ # @return [Class]
69
+ def builder_for(type)
70
+ case type
71
+ when :command
72
+ Command::CommandMessage.builder
73
+ when :domain_event
74
+ Domain::DomainEventMessage.builder
75
+ when :event
76
+ Domain::EventMessage.builder
77
+ else
78
+ raise ArgumentError, 'Unknown message type'
79
+ end
80
+ end
81
+ end # JsonMessageUnpacker
82
+ end # Partitioning
83
+ end # Synapse
@@ -0,0 +1,27 @@
1
+ module Synapse
2
+ module Partitioning
3
+ # Represents a mechanism for packing a message into a string so that it can be either stored
4
+ # in a file or passed over a transport mechanism to be unpacked at the receiver
5
+ #
6
+ # This implementation simply returns the message as-is
7
+ class MessagePacker
8
+ # @param [Message] unpacked
9
+ # @return [String]
10
+ def pack_message(unpacked)
11
+ unpacked
12
+ end
13
+ end
14
+
15
+ # Represents a mechanism for unpacking a message that has been packed for storage in a file
16
+ # or transport from a producer
17
+ #
18
+ # This implementation simply returns the message as-is
19
+ class MessageUnpacker
20
+ # @param [String] packed
21
+ # @return [Message]
22
+ def unpack_message(packed)
23
+ packed
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module Synapse
2
+ module Partitioning
3
+ # Represents a mechanism for taking messages off a queue and handling the acknowledgement
4
+ # or rejection of each message
5
+ #
6
+ # @abstract
7
+ class QueueReader
8
+ # Subscribes the given handler to the queue
9
+ #
10
+ # Depending on the implementation, this method may or may not return immediately. It should
11
+ # be assumed that the method will block until a message is received and then will go back
12
+ # to blocking after the given callback is invoked.
13
+ #
14
+ # @abstract
15
+ # @yield [MessageReceipt] Receipt of the message taken off the queue
16
+ # @return [undefined]
17
+ def subscribe(&handler); end
18
+
19
+ # Acknowledges the message, removing it from the original queue
20
+ #
21
+ # @param [MessageReceipt] receipt
22
+ # @return [undefined]
23
+ def ack_message(receipt); end
24
+
25
+ # Attempts to notify the original queue that the message was not processed
26
+ #
27
+ # @param [MessageReceipt] receipt
28
+ # @return [undefined]
29
+ def nack_message(receipt); end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ module Synapse
2
+ module Partitioning
3
+ # Represents a mechanism for writing packed messages to a queue
4
+ # @abstract
5
+ class QueueWriter
6
+ # Enqueues the given message
7
+ #
8
+ # Depending on the implementation, this method may or may not block until the message has
9
+ # been enqueued.
10
+ #
11
+ # @param [Object] packed
12
+ # @param [Message] unpacked
13
+ # @return [undefined]
14
+ def put_message(packed, unpacked); end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module Synapse
2
+ module Partitioning
3
+ extend ActiveSupport::Autoload
4
+
5
+ # Optional queues
6
+ autoload :AMQP
7
+
8
+ autoload :MemoryQueueReader
9
+ autoload :MemoryQueueWriter
10
+
11
+ # Optional message packing
12
+ autoload :JsonMessagePacker, 'synapse/partitioning/packing/json_packer'
13
+ autoload :JsonMessageUnpacker, 'synapse/partitioning/packing/json_unpacker'
14
+ end
15
+ end
16
+
17
+ require 'synapse/partitioning/message_receipt'
18
+ require 'synapse/partitioning/packing'
19
+ require 'synapse/partitioning/queue_reader'
20
+ require 'synapse/partitioning/queue_writer'
@@ -0,0 +1,4 @@
1
+ require 'synapse/process_manager/correlation'
2
+ require 'synapse/process_manager/correlation_resolver'
3
+ require 'synapse/process_manager/correlation_set'
4
+ require 'synapse/process_manager/process'
@@ -26,10 +26,9 @@ module Synapse
26
26
  begin
27
27
  aggregate = perform_load aggregate_id, expected_version
28
28
 
29
- aggregate = register_aggregate aggregate
30
- register_listener LockCleaningUnitOfWorkListener.new aggregate_id, @lock_manager
31
-
32
- aggregate
29
+ register_aggregate(aggregate).tap do
30
+ register_listener LockCleaningUnitOfWorkListener.new aggregate_id, @lock_manager
31
+ end
33
32
  rescue
34
33
  @logger.debug 'Excepton raised while loading an aggregate, releasing lock'
35
34
 
@@ -70,10 +69,6 @@ module Synapse
70
69
  # @param [Integer] expected_version
71
70
  # @return [AggregateRoot]
72
71
  def perform_load(aggregate_id, expected_version); end
73
-
74
- def logger
75
-
76
- end
77
72
  end
78
73
 
79
74
  # Unit of work listener that releases the lock on an aggregate when the unit of work
@@ -0,0 +1,7 @@
1
+ require 'synapse/repository/errors'
2
+
3
+ require 'synapse/repository/lock_manager'
4
+ require 'synapse/repository/pessimistic_lock_manager'
5
+
6
+ require 'synapse/repository/repository'
7
+ require 'synapse/repository/locking'
@@ -0,0 +1,28 @@
1
+ require 'bson'
2
+
3
+ module Synapse
4
+ module Serialization
5
+ # Converter that converts an ordered hash from BSON into a regular Ruby hash
6
+ class OrderedHashToHashConverter
7
+ include Converter
8
+
9
+ converts BSON::OrderedHash, Hash
10
+
11
+ # @param [Object] original
12
+ # @return [Object]
13
+ def convert_content(original)
14
+ converted = Hash.new
15
+
16
+ original.each do |key, value|
17
+ if value.is_a? BSON::OrderedHash
18
+ value = convert_content value
19
+ end
20
+
21
+ converted[key] = value
22
+ end
23
+
24
+ converted
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Implementation of a serializer that uses the hashing features of ActiveModel and Virtus
4
+ #
5
+ # If the object being serialized or deserialized is a hash, it will be untouched
6
+ #
7
+ # Objects being serialized by this implementation must meet the following requirements:
8
+ # * Respond to #attributes and #attributes=
9
+ # * #attributes= must work without the deserializer calling #initialize
10
+ #
11
+ # If either one of these are not met, the serializer may fail instantly or the deserialized
12
+ # object could be put in a bad state
13
+ class AttributeSerializer < Serializer
14
+ # This serializer doesn't provide any configuration options
15
+
16
+ protected
17
+
18
+ # @param [Object] content
19
+ # @return [Object]
20
+ def perform_serialize(content)
21
+ if content.is_a? Hash
22
+ content
23
+ else
24
+ content.attributes
25
+ end
26
+ end
27
+
28
+ # @param [Object] content
29
+ # @param [Class] type
30
+ # @return [Object]
31
+ def perform_deserialize(content, type)
32
+ if Hash == type
33
+ content
34
+ else
35
+ # Allocate the target object but don't call #initialize
36
+ object = type.allocate
37
+ object.attributes = content
38
+ object
39
+ end
40
+ end
41
+
42
+ # @return [Class]
43
+ def native_content_type
44
+ Hash
45
+ end
46
+ end
47
+ end
48
+ end
@@ -36,7 +36,7 @@ module Synapse
36
36
  # @param [Class] representation_type
37
37
  # @return [Boolean]
38
38
  def can_serialize_to?(representation_type)
39
- converter_factory.has_converter?(String, representation_type)
39
+ converter_factory.has_converter?(native_content_type, representation_type)
40
40
  end
41
41
 
42
42
  # @param [SerializedType] serialized_type
@@ -0,0 +1,46 @@
1
+ module Synapse
2
+ module Serialization
3
+ extend ActiveSupport::Autoload
4
+
5
+ # Optional converters
6
+ autoload_at 'synapse/serialization/converter/json' do
7
+ autoload :JsonToObjectConverter
8
+ autoload :ObjectToJsonConverter
9
+ end
10
+
11
+ autoload_at 'synapse/serialization/converter/ox' do
12
+ autoload :XmlToOxDocumentConverter
13
+ autoload :OxDocumentToXmlConverter
14
+ end
15
+
16
+ autoload :OrderedHashToHashConverter, 'synapse/serialization/converter/bson'
17
+
18
+ # Optional serializers
19
+ autoload :AttributeSerializer, 'synapse/serialization/serializer/attribute'
20
+ autoload :OjSerializer, 'synapse/serialization/serializer/oj'
21
+ autoload :OxSerializer, 'synapse/serialization/serializer/ox'
22
+ autoload :MarshalSerializer, 'synapse/serialization/serializer/marshal'
23
+ end
24
+ end
25
+
26
+ require 'synapse/serialization/converter'
27
+ require 'synapse/serialization/converter/chain'
28
+ require 'synapse/serialization/converter/factory'
29
+ require 'synapse/serialization/converter/identity'
30
+
31
+ require 'synapse/serialization/errors'
32
+
33
+ require 'synapse/serialization/lazy_object'
34
+ require 'synapse/serialization/revision_resolver'
35
+ require 'synapse/serialization/serialized_object'
36
+ require 'synapse/serialization/serialized_type'
37
+ require 'synapse/serialization/serializer'
38
+
39
+ require 'synapse/serialization/message/data'
40
+ require 'synapse/serialization/message/metadata'
41
+ require 'synapse/serialization/message/serialization_aware'
42
+ require 'synapse/serialization/message/serialization_aware_message'
43
+ require 'synapse/serialization/message/serialized_message_builder'
44
+ require 'synapse/serialization/message/serialized_message'
45
+ require 'synapse/serialization/message/serialized_object_cache'
46
+ require 'synapse/serialization/message/serializer'
@@ -15,13 +15,13 @@ module Synapse
15
15
  # @return [UnitOfWork]
16
16
  def create
17
17
  unit = UnitOfWork.new @provider
18
+ unit.tap do
19
+ if @transaction_manager
20
+ unit.transaction_manager = @transaction_manager
21
+ end
18
22
 
19
- if @transaction_manager
20
- unit.transaction_manager = @transaction_manager
23
+ unit.start
21
24
  end
22
-
23
- unit.start
24
- unit
25
25
  end
26
26
  end
27
27
  end
@@ -48,13 +48,13 @@ module Synapse
48
48
  return similar
49
49
  end
50
50
 
51
- aggregate.add_registration_listener do |event|
52
- publish_event event, event_bus
53
- end
54
-
55
- @aggregates.store aggregate, storage_listener
51
+ aggregate.tap do
52
+ aggregate.add_registration_listener do |event|
53
+ publish_event event, event_bus
54
+ end
56
55
 
57
- aggregate
56
+ @aggregates.store aggregate, storage_listener
57
+ end
58
58
  end
59
59
 
60
60
  # Buffers an event for publication to the given event bus until this unit of work is
@@ -65,15 +65,15 @@ module Synapse
65
65
  # @return [EventMessage] The event that will be published to the event bus
66
66
  def publish_event(event, event_bus)
67
67
  event = @listeners.on_event_registered self, event
68
+ event.tap do
69
+ begin
70
+ events = @events.fetch event_bus
71
+ rescue KeyError
72
+ events = @events.store event_bus, Array.new
73
+ end
68
74
 
69
- begin
70
- events = @events.fetch event_bus
71
- rescue KeyError
72
- events = @events.store event_bus, Array.new
75
+ events.push event
73
76
  end
74
-
75
- events.push event
76
- event
77
77
  end
78
78
 
79
79
  # Sets the transaction manager that will be used by this unit of work
@@ -0,0 +1,8 @@
1
+ require 'synapse/uow/factory'
2
+ require 'synapse/uow/listener'
3
+ require 'synapse/uow/listener_collection'
4
+ require 'synapse/uow/nesting'
5
+ require 'synapse/uow/provider'
6
+ require 'synapse/uow/storage_listener'
7
+ require 'synapse/uow/transaction_manager'
8
+ require 'synapse/uow/uow'
@@ -63,7 +63,7 @@ module Synapse
63
63
 
64
64
  if upcaster.can_upcast? serialized_type
65
65
  serialized_object = converter_factory.convert serialized_object, upcaster.expected_content_type
66
- expected_types = upcaster.upcast_type(serialized_type)
66
+ expected_types = upcaster.upcast_type serialized_type
67
67
 
68
68
  upcast_objects.concat(perform_upcast(upcaster, serialized_object, expected_types, upcast_context))
69
69
  else
@@ -0,0 +1,5 @@
1
+ require 'synapse/upcasting/context'
2
+ require 'synapse/upcasting/chain'
3
+ require 'synapse/upcasting/data'
4
+ require 'synapse/upcasting/upcaster'
5
+ require 'synapse/upcasting/single_upcaster'
@@ -1,3 +1,3 @@
1
1
  module Synapse
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -0,0 +1,3 @@
1
+ require 'synapse/wiring/message_wiring'
2
+ require 'synapse/wiring/wire'
3
+ require 'synapse/wiring/wire_registry'