synapse-core 0.1.2

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 (155) hide show
  1. data/lib/synapse.rb +351 -0
  2. data/lib/synapse/command/command_bus.rb +45 -0
  3. data/lib/synapse/command/command_callback.rb +18 -0
  4. data/lib/synapse/command/command_filter.rb +17 -0
  5. data/lib/synapse/command/command_handler.rb +13 -0
  6. data/lib/synapse/command/dispatch_interceptor.rb +16 -0
  7. data/lib/synapse/command/duplication.rb +43 -0
  8. data/lib/synapse/command/errors.rb +27 -0
  9. data/lib/synapse/command/filters/validation.rb +32 -0
  10. data/lib/synapse/command/gateway.rb +34 -0
  11. data/lib/synapse/command/interceptor_chain.rb +31 -0
  12. data/lib/synapse/command/interceptors/serialization.rb +35 -0
  13. data/lib/synapse/command/message.rb +19 -0
  14. data/lib/synapse/command/rollback_policy.rb +22 -0
  15. data/lib/synapse/command/simple_command_bus.rb +138 -0
  16. data/lib/synapse/command/wiring.rb +47 -0
  17. data/lib/synapse/domain/aggregate_root.rb +121 -0
  18. data/lib/synapse/domain/event_container.rb +127 -0
  19. data/lib/synapse/domain/message.rb +82 -0
  20. data/lib/synapse/domain/message_builder.rb +34 -0
  21. data/lib/synapse/domain/stream.rb +108 -0
  22. data/lib/synapse/duplication.rb +60 -0
  23. data/lib/synapse/errors.rb +13 -0
  24. data/lib/synapse/event_bus/event_bus.rb +40 -0
  25. data/lib/synapse/event_bus/event_listener.rb +16 -0
  26. data/lib/synapse/event_bus/event_listener_proxy.rb +12 -0
  27. data/lib/synapse/event_bus/simple_event_bus.rb +69 -0
  28. data/lib/synapse/event_bus/wiring.rb +23 -0
  29. data/lib/synapse/event_sourcing/aggregate_factory.rb +69 -0
  30. data/lib/synapse/event_sourcing/aggregate_root.rb +104 -0
  31. data/lib/synapse/event_sourcing/conflict_resolver.rb +80 -0
  32. data/lib/synapse/event_sourcing/entity.rb +64 -0
  33. data/lib/synapse/event_sourcing/member.rb +72 -0
  34. data/lib/synapse/event_sourcing/repository.rb +119 -0
  35. data/lib/synapse/event_sourcing/snapshot/count_stream.rb +86 -0
  36. data/lib/synapse/event_sourcing/snapshot/count_trigger.rb +91 -0
  37. data/lib/synapse/event_sourcing/snapshot/taker.rb +73 -0
  38. data/lib/synapse/event_sourcing/storage_listener.rb +34 -0
  39. data/lib/synapse/event_sourcing/stream_decorator.rb +25 -0
  40. data/lib/synapse/event_store/errors.rb +16 -0
  41. data/lib/synapse/event_store/event_store.rb +43 -0
  42. data/lib/synapse/event_store/in_memory.rb +59 -0
  43. data/lib/synapse/event_store/mongo/cursor_event_stream.rb +63 -0
  44. data/lib/synapse/event_store/mongo/event_store.rb +86 -0
  45. data/lib/synapse/event_store/mongo/per_commit_strategy.rb +253 -0
  46. data/lib/synapse/event_store/mongo/per_event_strategy.rb +143 -0
  47. data/lib/synapse/event_store/mongo/storage_strategy.rb +113 -0
  48. data/lib/synapse/event_store/mongo/template.rb +73 -0
  49. data/lib/synapse/identifier.rb +23 -0
  50. data/lib/synapse/message.rb +101 -0
  51. data/lib/synapse/message_builder.rb +38 -0
  52. data/lib/synapse/process_manager/correlation.rb +32 -0
  53. data/lib/synapse/process_manager/correlation_resolver.rb +14 -0
  54. data/lib/synapse/process_manager/correlation_set.rb +58 -0
  55. data/lib/synapse/process_manager/process.rb +71 -0
  56. data/lib/synapse/repository/errors.rb +26 -0
  57. data/lib/synapse/repository/lock_manager.rb +40 -0
  58. data/lib/synapse/repository/locking.rb +97 -0
  59. data/lib/synapse/repository/pessimistic_lock_manager.rb +61 -0
  60. data/lib/synapse/repository/repository.rb +109 -0
  61. data/lib/synapse/serialization/converter.rb +39 -0
  62. data/lib/synapse/serialization/converter/chain.rb +45 -0
  63. data/lib/synapse/serialization/converter/factory.rb +68 -0
  64. data/lib/synapse/serialization/converter/identity.rb +29 -0
  65. data/lib/synapse/serialization/converter/json.rb +31 -0
  66. data/lib/synapse/serialization/converter/ox.rb +31 -0
  67. data/lib/synapse/serialization/errors.rb +12 -0
  68. data/lib/synapse/serialization/lazy_object.rb +61 -0
  69. data/lib/synapse/serialization/message/data.rb +25 -0
  70. data/lib/synapse/serialization/message/metadata.rb +13 -0
  71. data/lib/synapse/serialization/message/serialization_aware.rb +17 -0
  72. data/lib/synapse/serialization/message/serialization_aware_message.rb +66 -0
  73. data/lib/synapse/serialization/message/serialized_message.rb +201 -0
  74. data/lib/synapse/serialization/message/serialized_message_builder.rb +64 -0
  75. data/lib/synapse/serialization/message/serialized_object_cache.rb +50 -0
  76. data/lib/synapse/serialization/message/serializer.rb +47 -0
  77. data/lib/synapse/serialization/revision_resolver.rb +30 -0
  78. data/lib/synapse/serialization/serialized_object.rb +37 -0
  79. data/lib/synapse/serialization/serialized_type.rb +31 -0
  80. data/lib/synapse/serialization/serializer.rb +98 -0
  81. data/lib/synapse/serialization/serializer/marshal.rb +32 -0
  82. data/lib/synapse/serialization/serializer/oj.rb +34 -0
  83. data/lib/synapse/serialization/serializer/ox.rb +31 -0
  84. data/lib/synapse/uow/factory.rb +28 -0
  85. data/lib/synapse/uow/listener.rb +79 -0
  86. data/lib/synapse/uow/listener_collection.rb +93 -0
  87. data/lib/synapse/uow/nesting.rb +262 -0
  88. data/lib/synapse/uow/provider.rb +71 -0
  89. data/lib/synapse/uow/storage_listener.rb +14 -0
  90. data/lib/synapse/uow/transaction_manager.rb +27 -0
  91. data/lib/synapse/uow/uow.rb +178 -0
  92. data/lib/synapse/upcasting/chain.rb +78 -0
  93. data/lib/synapse/upcasting/context.rb +58 -0
  94. data/lib/synapse/upcasting/data.rb +30 -0
  95. data/lib/synapse/upcasting/single_upcaster.rb +57 -0
  96. data/lib/synapse/upcasting/upcaster.rb +55 -0
  97. data/lib/synapse/version.rb +3 -0
  98. data/lib/synapse/wiring/message_wiring.rb +41 -0
  99. data/lib/synapse/wiring/wire.rb +55 -0
  100. data/lib/synapse/wiring/wire_registry.rb +61 -0
  101. data/test/command/duplication_test.rb +54 -0
  102. data/test/command/gateway_test.rb +25 -0
  103. data/test/command/interceptor_chain_test.rb +26 -0
  104. data/test/command/serialization_test.rb +37 -0
  105. data/test/command/simple_command_bus_test.rb +141 -0
  106. data/test/command/validation_test.rb +42 -0
  107. data/test/command/wiring_test.rb +73 -0
  108. data/test/domain/aggregate_root_test.rb +57 -0
  109. data/test/domain/fixtures.rb +31 -0
  110. data/test/domain/message_test.rb +61 -0
  111. data/test/domain/stream_test.rb +35 -0
  112. data/test/duplication_test.rb +40 -0
  113. data/test/event_bus/wiring_test.rb +46 -0
  114. data/test/event_sourcing/aggregate_factory_test.rb +28 -0
  115. data/test/event_sourcing/aggregate_root_test.rb +76 -0
  116. data/test/event_sourcing/entity_test.rb +34 -0
  117. data/test/event_sourcing/fixtures.rb +85 -0
  118. data/test/event_sourcing/repository_test.rb +102 -0
  119. data/test/event_sourcing/snapshot/aggregate_taker_test.rb +39 -0
  120. data/test/event_sourcing/snapshot/deferred_taker_test.rb +19 -0
  121. data/test/event_sourcing/snapshot/integration_test.rb +65 -0
  122. data/test/event_sourcing/storage_listener_test.rb +77 -0
  123. data/test/event_store/in_memory_test.rb +47 -0
  124. data/test/process_manager/correlation_set_test.rb +49 -0
  125. data/test/process_manager/correlation_test.rb +24 -0
  126. data/test/process_manager/process_test.rb +52 -0
  127. data/test/repository/locking_test.rb +101 -0
  128. data/test/serialization/converter/factory_test.rb +33 -0
  129. data/test/serialization/converter/identity_test.rb +17 -0
  130. data/test/serialization/converter/json_test.rb +31 -0
  131. data/test/serialization/converter/ox_test.rb +40 -0
  132. data/test/serialization/fixtures.rb +17 -0
  133. data/test/serialization/lazy_object_test.rb +32 -0
  134. data/test/serialization/message/metadata_test.rb +19 -0
  135. data/test/serialization/message/serialization_aware_message_test.rb +88 -0
  136. data/test/serialization/message/serialized_message_builder_test.rb +41 -0
  137. data/test/serialization/message/serialized_message_test.rb +140 -0
  138. data/test/serialization/message/serializer_test.rb +50 -0
  139. data/test/serialization/revision_resolver_test.rb +12 -0
  140. data/test/serialization/serialized_object_test.rb +36 -0
  141. data/test/serialization/serialized_type_test.rb +27 -0
  142. data/test/serialization/serializer/marshal_test.rb +22 -0
  143. data/test/serialization/serializer/oj_test.rb +24 -0
  144. data/test/serialization/serializer/ox_test.rb +36 -0
  145. data/test/serialization/serializer_test.rb +20 -0
  146. data/test/test_helper.rb +19 -0
  147. data/test/uow/factory_test.rb +23 -0
  148. data/test/uow/outer_commit_listener_test.rb +50 -0
  149. data/test/uow/provider_test.rb +70 -0
  150. data/test/uow/uow_test.rb +337 -0
  151. data/test/upcasting/chain_test.rb +29 -0
  152. data/test/upcasting/fixtures.rb +66 -0
  153. data/test/wiring/wire_registry_test.rb +60 -0
  154. data/test/wiring/wire_test.rb +51 -0
  155. metadata +263 -0
@@ -0,0 +1,109 @@
1
+ module Synapse
2
+ module Repository
3
+ # Represents a mechanism for loading and storing aggregates
4
+ # @abstract
5
+ class Repository
6
+ # @return [EventBus]
7
+ attr_accessor :event_bus
8
+
9
+ # @return [UnitOfWorkProvider]
10
+ attr_accessor :unit_provider
11
+
12
+ # Loads an aggregate with the given aggregate identifier
13
+ #
14
+ # If an expected version is specified and the aggregate's actual version doesn't equal the
15
+ # expected version, the implementation can choose to do one of the following:
16
+ #
17
+ # - Raise an exception immediately
18
+ # - Raise an exception at any other time while the aggregate is registered with the current
19
+ # unit of work.
20
+ #
21
+ # @abstract
22
+ # @raise [AggregateNotFoundError]
23
+ # If the aggregate with the given identifier could not be found
24
+ # @raise [ConflictingModificationError]
25
+ # If the expected version doesn't match the aggregate's actual version
26
+ # @param [Object] aggregate_id
27
+ # @param [Integer] expected_version If this is nil, no version validation is performed
28
+ # @return [AggregateRoot]
29
+ def load(aggregate_id, expected_version = nil); end
30
+
31
+ # Adds a new, unmanaged aggregate to the repository
32
+ #
33
+ # This method will not force the repository to save the aggregate immediately. Instead, it is
34
+ # registered with the current unit of work. To force storage of an aggregate, commit the
35
+ # current unit of work.
36
+ #
37
+ # @abstract
38
+ # @raise [ArgumentError] If the version of the aggregate is not null
39
+ # @param [AggregateRoot] aggregate
40
+ # @return [undefined]
41
+ def add(aggregate); end
42
+
43
+ protected
44
+
45
+ # Returns the type of aggregate that this repository handles
46
+ #
47
+ # @abstract
48
+ # @return [Class]
49
+ def aggregate_type; end
50
+
51
+ # Returns the listener that handles aggregate storage
52
+ #
53
+ # @abstract
54
+ # @return [StorageListener]
55
+ def storage_listener; end
56
+
57
+ # Asserts that an aggregate being added is compatible with this repository and is newly
58
+ # created
59
+ #
60
+ # @raise [ArgumentError] If aggregate is not of the correct type
61
+ # @raise [ArgumentError] If aggregate has a version number
62
+ # @param [AggregateRoot] aggregate
63
+ # @return [undefined]
64
+ def assert_compatible(aggregate)
65
+ unless aggregate.is_a? aggregate_type
66
+ raise ArgumentError, 'Incompatible aggregate type'
67
+ end
68
+
69
+ if aggregate.version
70
+ raise ArgumentError, 'Only newly created aggregates may be added'
71
+ end
72
+ end
73
+
74
+ # Asserts that a loaded aggregate has the expected version
75
+ #
76
+ # @raise [ConflictingAggregateVersionError]
77
+ # If aggregate version is later than the expected version
78
+ # @param [AggregateRoot] aggregate
79
+ # @param [Integer] expected_version
80
+ # @return [undefined]
81
+ def assert_version_expected(aggregate, expected_version)
82
+ if expected_version and aggregate.version and aggregate.version > expected_version
83
+ raise ConflictingAggregateVersionError.new aggregate, expected_version
84
+ end
85
+ end
86
+
87
+ # Registers the given aggregate with the current unit of work
88
+ #
89
+ # @param [AggregateRoot] aggregate
90
+ # @return [undefined]
91
+ def register_aggregate(aggregate)
92
+ current_unit.register_aggregate aggregate, @event_bus, storage_listener
93
+ end
94
+
95
+ # Registers the given unit of work listener with the current unit of work
96
+ #
97
+ # @param [UnitOfWorkListener] listener
98
+ # @return [undefined]
99
+ def register_listener(listener)
100
+ current_unit.register_listener listener
101
+ end
102
+
103
+ # @return [UnitOfWork]
104
+ def current_unit
105
+ @unit_provider.current
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,39 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Represents a mechanism for converting content of one type to another type for the purposes
4
+ # of serialization, deserialization and upcasting.
5
+ module Converter
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :source_type
10
+ class_attribute :target_type
11
+ end
12
+
13
+ module ClassMethods
14
+ # @param [Class] source_type
15
+ # @param [Class] target_type
16
+ # @return [undefined]
17
+ def converts(source_type, target_type)
18
+ self.source_type = source_type
19
+ self.target_type = target_type
20
+ end
21
+ end
22
+
23
+ def initialize(options = {})
24
+ @options = options
25
+ end
26
+
27
+ # @param [SerializedObject] original
28
+ # @return [SerializedObject]
29
+ def convert(original)
30
+ SerializedObject.new(convert_content(original.content), target_type, original.type)
31
+ end
32
+
33
+ # @abstract
34
+ # @param [Object] original
35
+ # @return [Object]
36
+ def convert_content(original); end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Converter implementation that chains together two or more converters
4
+ class ConverterChain
5
+ include Converter
6
+
7
+ # @return [Array<Converter>]
8
+ attr_reader :delegates
9
+
10
+ # @return [Class]
11
+ attr_reader :source_type
12
+
13
+ # @return [Class]
14
+ attr_reader :target_type
15
+
16
+ # @param [Array<Converter>] delegates
17
+ # @return [undefined]
18
+ def initialize(delegates)
19
+ @delegates = delegates
20
+ @source_type = delegates.first.source_type
21
+ @target_type = delegates.last.target_type
22
+ end
23
+
24
+ # @param [SerializedObject] original
25
+ # @return [SerializedObject]
26
+ def convert(original)
27
+ intermediate = original
28
+ @delegates.each do |delegate|
29
+ intermediate = delegate.convert intermediate
30
+ end
31
+ intermediate
32
+ end
33
+
34
+ # @param [Object] original
35
+ # @return [Object]
36
+ def convert_content(original)
37
+ intermediate = original
38
+ @delegates.each do |delegate|
39
+ intermediate = delegate.convert_content intermediate
40
+ end
41
+ intermediate
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,68 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Represents a mechanism for storing and retrieving converters capable of converting content
4
+ # of one type to another type, for the purpose of serialization and upcasting.
5
+ class ConverterFactory
6
+ attr_reader :converters
7
+
8
+ def initialize
9
+ @converters = Array.new
10
+ end
11
+
12
+ # Adds the given converter to this converter factory
13
+ #
14
+ # @param [Converter] converter
15
+ # @return [undefined]
16
+ def register(converter)
17
+ @converters.push converter
18
+ end
19
+
20
+ # Convenience method for converting a given serialized object to the given target type
21
+ #
22
+ # @param [SerializedObject] serialized_object
23
+ # @param [Class] target_type
24
+ # @return [SerializedObject]
25
+ def convert(serialized_object, target_type)
26
+ converter = converter serialized_object.content_type, target_type
27
+ converter.convert serialized_object
28
+ end
29
+
30
+ # Returns a converter that is capable of converting content of the given source type to
31
+ # the given target type, if one exists.
32
+ #
33
+ # @raise [ConversionError] If no converter is capable of performing the conversion
34
+ # @param [Class] source_type
35
+ # @param [Class] target_type
36
+ # @return [Converter]
37
+ def converter(source_type, target_type)
38
+ if source_type == target_type
39
+ return IdentityConverter.new source_type
40
+ end
41
+
42
+ @converters.each do |converter|
43
+ if converter.source_type == source_type && converter.target_type == target_type
44
+ return converter
45
+ end
46
+ end
47
+
48
+ raise ConversionError, 'No converter capable of [%s] -> [%s]' % [source_type, target_type]
49
+ end
50
+
51
+ # Returns true if this factory contains a converter capable of converting content from the
52
+ # given source type to the given target type.
53
+ #
54
+ # @param [Class] source_type
55
+ # @param [Class] target_type
56
+ # @return [Boolean]
57
+ def has_converter?(source_type, target_type)
58
+ if source_type == target_type
59
+ return true
60
+ end
61
+
62
+ @converters.any? do |converter|
63
+ converter.source_type == source_type && converter.target_type == target_type
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,29 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Implementation of a converter that does no conversion
4
+ class IdentityConverter
5
+ include Converter
6
+
7
+ attr_reader :source_type
8
+ attr_reader :target_type
9
+
10
+ # @param [Class] type
11
+ # @return [undefined]
12
+ def initialize(type)
13
+ @source_type = @target_type = type
14
+ end
15
+
16
+ # @param [SerializedObject] original
17
+ # @return [SerializedObject]
18
+ def convert(original)
19
+ original
20
+ end
21
+
22
+ # @param [Object] original
23
+ # @return [Object]
24
+ def convert_content(original)
25
+ original
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+
3
+ module Synapse
4
+ module Serialization
5
+ # Converter that converts a JSON string into a Ruby data structure
6
+ class JsonToObjectConverter
7
+ include Converter
8
+
9
+ converts String, Object
10
+
11
+ # @param [Object] original
12
+ # @return [Object]
13
+ def convert_content(original)
14
+ JSON.parse original, @options
15
+ end
16
+ end
17
+
18
+ # Converter that converts a Ruby data structure into a JSON string
19
+ class ObjectToJsonConverter
20
+ include Converter
21
+
22
+ converts Object, String
23
+
24
+ # @param [Object] original
25
+ # @return [Object]
26
+ def convert_content(original)
27
+ JSON.generate original, @options
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'ox'
2
+
3
+ module Synapse
4
+ module Serialization
5
+ # Converter that converts an Ox document into an XML string
6
+ class OxDocumentToXmlConverter
7
+ include Converter
8
+
9
+ converts Ox::Document, String
10
+
11
+ # @param [Object] original
12
+ # @return [Object]
13
+ def convert_content(original)
14
+ Ox.dump original, @options
15
+ end
16
+ end
17
+
18
+ # Converter that converts an XML string into an Ox document
19
+ class XmlToOxDocumentConverter
20
+ include Converter
21
+
22
+ converts String, Ox::Document
23
+
24
+ # @param [Object] original
25
+ # @return [Object]
26
+ def convert_content(original)
27
+ Ox.load original, @options
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Raised when conversion between two types is not supported
4
+ class ConversionError < NonTransientError; end
5
+
6
+ # Raised when an error occurs during serialization
7
+ class SerializationError < NonTransientError; end
8
+
9
+ # Raised when a serialized type can't be found in the current environment
10
+ class UnknownSerializedTypeError < SerializationError; end
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Provides a generic lazy deserializing object
4
+ class LazyObject
5
+ # @return [SerializedObject]
6
+ attr_reader :serialized_object
7
+
8
+ # @return [Serializer]
9
+ attr_reader :serializer
10
+
11
+ # @return [Class]
12
+ attr_reader :type
13
+
14
+ # @param [SerializedObject] serialized_object
15
+ # @param [Serializer] serializer
16
+ # @return [undefined]
17
+ def initialize(serialized_object, serializer)
18
+ @serialized_object = serialized_object
19
+ @serializer = serializer
20
+ @type = serializer.class_for serialized_object.type
21
+ end
22
+
23
+ # Returns the deserialized version of the contained serialized object
24
+ # @return [Object]
25
+ def deserialized
26
+ @deserialized ||= @serializer.deserialize @serialized_object
27
+ end
28
+
29
+ # Returns true if this object has been deserialized already
30
+ # @return [Boolean]
31
+ def deserialized?
32
+ !!@deserialized
33
+ end
34
+ end
35
+
36
+ # Deserialized object that has (nearly) the same interface as a lazy object
37
+ class DeserializedObject
38
+ # @return [Object]
39
+ attr_reader :deserialized
40
+
41
+ # @return [Serializer] This will always be nil
42
+ attr_reader :serializer
43
+
44
+ # @return [Class]
45
+ attr_reader :type
46
+
47
+ # @param [Object] deserialized
48
+ # @return [undefined]
49
+ def initialize(deserialized)
50
+ @deserialized = deserialized
51
+ @type = deserialized.class
52
+ end
53
+
54
+ # Returns true if this object has been deserialized already; always true
55
+ # @return [Boolean]
56
+ def deserialized?
57
+ true
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Describes the properties that a serialized domain event should have
4
+ # @abstract
5
+ class SerializedDomainEventData
6
+ # @return [String] Identifier of the serialized event
7
+ def id; end
8
+
9
+ # @return [SerializedObject] Serialized metadata of the serialized event
10
+ def metadata; end
11
+
12
+ # @return [SerializedObject] Serialized payload of the serialized event
13
+ def payload; end
14
+
15
+ # @return [Time] Timestamp of the serialized event
16
+ def timestamp; end
17
+
18
+ # @return [Object] Identifier of the aggregate that the event was applied to
19
+ def aggregate_id; end
20
+
21
+ # @return [Integer] Sequence number of the event in the aggregate
22
+ def sequence_number; end
23
+ end
24
+ end
25
+ end