synapse-core 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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'