ruby_event_store 0.30.0 → 1.3.1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +9 -1
  3. data/Makefile +15 -62
  4. data/lib/ruby_event_store/batch_enumerator.rb +15 -5
  5. data/lib/ruby_event_store/broker.rb +45 -0
  6. data/lib/ruby_event_store/client.rb +220 -130
  7. data/lib/ruby_event_store/composed_dispatcher.rb +24 -0
  8. data/lib/ruby_event_store/constants.rb +2 -0
  9. data/lib/ruby_event_store/correlated_commands.rb +42 -0
  10. data/lib/ruby_event_store/dispatcher.rb +20 -0
  11. data/lib/ruby_event_store/errors.rb +16 -16
  12. data/lib/ruby_event_store/event.rb +70 -14
  13. data/lib/ruby_event_store/expected_version.rb +2 -0
  14. data/lib/ruby_event_store/immediate_async_dispatcher.rb +17 -0
  15. data/lib/ruby_event_store/in_memory_repository.rb +45 -17
  16. data/lib/ruby_event_store/instrumented_dispatcher.rb +23 -0
  17. data/lib/ruby_event_store/instrumented_repository.rb +63 -0
  18. data/lib/ruby_event_store/link_by_metadata.rb +57 -0
  19. data/lib/ruby_event_store/mappers/default.rb +10 -26
  20. data/lib/ruby_event_store/mappers/encryption_key.rb +74 -0
  21. data/lib/ruby_event_store/mappers/encryption_mapper.rb +16 -0
  22. data/lib/ruby_event_store/mappers/forgotten_data.rb +30 -0
  23. data/lib/ruby_event_store/mappers/in_memory_encryption_key_repository.rb +34 -0
  24. data/lib/ruby_event_store/mappers/instrumented_mapper.rb +28 -0
  25. data/lib/ruby_event_store/mappers/json_mapper.rb +16 -0
  26. data/lib/ruby_event_store/mappers/null_mapper.rb +5 -8
  27. data/lib/ruby_event_store/mappers/pipeline.rb +31 -0
  28. data/lib/ruby_event_store/mappers/pipeline_mapper.rb +22 -0
  29. data/lib/ruby_event_store/mappers/protobuf.rb +13 -67
  30. data/lib/ruby_event_store/mappers/transformation/domain_event.rb +26 -0
  31. data/lib/ruby_event_store/mappers/transformation/encryption.rb +128 -0
  32. data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +24 -0
  33. data/lib/ruby_event_store/mappers/transformation/item.rb +56 -0
  34. data/lib/ruby_event_store/mappers/transformation/proto_event.rb +17 -0
  35. data/lib/ruby_event_store/mappers/transformation/protobuf_encoder.rb +30 -0
  36. data/lib/ruby_event_store/mappers/transformation/protobuf_nested_struct_metadata.rb +30 -0
  37. data/lib/ruby_event_store/mappers/transformation/serialization.rb +34 -0
  38. data/lib/ruby_event_store/mappers/transformation/serialized_record.rb +27 -0
  39. data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +24 -0
  40. data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +24 -0
  41. data/lib/ruby_event_store/metadata.rb +4 -2
  42. data/lib/ruby_event_store/projection.rb +34 -12
  43. data/lib/ruby_event_store/serialized_record.rb +3 -1
  44. data/lib/ruby_event_store/spec/broker_lint.rb +92 -0
  45. data/lib/ruby_event_store/spec/dispatcher_lint.rb +4 -36
  46. data/lib/ruby_event_store/spec/event_lint.rb +71 -0
  47. data/lib/ruby_event_store/spec/event_repository_lint.rb +1092 -962
  48. data/lib/ruby_event_store/spec/mapper_lint.rb +17 -0
  49. data/lib/ruby_event_store/spec/scheduler_lint.rb +9 -0
  50. data/lib/ruby_event_store/spec/subscriptions_lint.rb +111 -0
  51. data/lib/ruby_event_store/specification.rb +201 -56
  52. data/lib/ruby_event_store/specification_reader.rb +43 -0
  53. data/lib/ruby_event_store/specification_result.rb +212 -0
  54. data/lib/ruby_event_store/stream.rb +2 -0
  55. data/lib/ruby_event_store/subscriptions.rb +110 -0
  56. data/lib/ruby_event_store/transform_keys.rb +31 -0
  57. data/lib/ruby_event_store/version.rb +3 -1
  58. data/lib/ruby_event_store.rb +34 -4
  59. data/ruby_event_store.gemspec +1 -10
  60. metadata +47 -126
  61. data/exe/res-deprecated-read-api-migrator +0 -19
  62. data/lib/ruby_event_store/deprecated_read_api_rewriter.rb +0 -67
  63. data/lib/ruby_event_store/deprecated_read_api_runner.rb +0 -64
  64. data/lib/ruby_event_store/deprecations.rb +0 -7
  65. data/lib/ruby_event_store/pub_sub/broker.rb +0 -73
  66. data/lib/ruby_event_store/pub_sub/dispatcher.rb +0 -25
  67. data/lib/ruby_event_store/spec/event_broker_lint.rb +0 -211
@@ -0,0 +1,17 @@
1
+ module RubyEventStore
2
+ RSpec.shared_examples :mapper do |mapper, domain_event|
3
+ specify "event_to_serialized_record returns instance of SerializedRecord" do
4
+ record = mapper.event_to_serialized_record(domain_event)
5
+
6
+ expect(record).to be_kind_of(SerializedRecord)
7
+ expect(record.event_id).to eq(domain_event.event_id)
8
+ expect(record.event_type).to eq(domain_event.event_type)
9
+ end
10
+
11
+ specify "serialize and deserialize gives equal event" do
12
+ record = mapper.event_to_serialized_record(domain_event)
13
+
14
+ expect(mapper.serialized_record_to_event(record)).to eq(domain_event)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ RSpec.shared_examples :scheduler do |scheduler|
2
+ specify "#call" do
3
+ expect(scheduler).to respond_to(:call).with(2).arguments
4
+ end
5
+
6
+ specify "#verify" do
7
+ expect(scheduler).to respond_to(:verify).with(1).argument
8
+ end
9
+ end
@@ -0,0 +1,111 @@
1
+ RSpec.shared_examples :subscriptions do |subscriptions_class|
2
+ Test1DomainEvent = Class.new(RubyEventStore::Event)
3
+ Test2DomainEvent = Class.new(RubyEventStore::Event)
4
+ Test3DomainEvent = Class.new(RubyEventStore::Event)
5
+
6
+ class TestHandler
7
+ def initialize
8
+ @events = []
9
+ end
10
+
11
+ def call(event)
12
+ @events << event
13
+ end
14
+
15
+ attr_reader :events
16
+ end
17
+
18
+ subject(:subscriptions) { subscriptions_class.new }
19
+
20
+ it 'returns all subscribed handlers' do
21
+ handler = TestHandler.new
22
+ another_handler = TestHandler.new
23
+ global_handler = TestHandler.new
24
+
25
+ subscriptions.add_subscription(handler, [Test1DomainEvent, Test3DomainEvent])
26
+ subscriptions.add_subscription(another_handler, [Test2DomainEvent])
27
+ subscriptions.add_global_subscription(global_handler)
28
+
29
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([handler, global_handler])
30
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([another_handler, global_handler])
31
+ expect(subscriptions.all_for('Test3DomainEvent')).to eq([handler, global_handler])
32
+ end
33
+
34
+ it 'returns subscribed thread handlers' do
35
+ handler = TestHandler.new
36
+ another_handler = TestHandler.new
37
+ global_handler = TestHandler.new
38
+
39
+ subscriptions.add_thread_subscription(handler, [Test1DomainEvent, Test3DomainEvent])
40
+ subscriptions.add_thread_subscription(another_handler, [Test2DomainEvent])
41
+ subscriptions.add_thread_global_subscription(global_handler)
42
+
43
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([global_handler, handler])
44
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([global_handler, another_handler])
45
+ expect(subscriptions.all_for('Test3DomainEvent')).to eq([global_handler, handler])
46
+ end
47
+
48
+ it 'returns lambda as an output of global subscribe methods' do
49
+ handler = TestHandler.new
50
+ result = subscriptions.add_global_subscription(handler)
51
+ expect(result).to respond_to(:call)
52
+ end
53
+
54
+ it 'returns lambda as an output of subscribe methods' do
55
+ handler = TestHandler.new
56
+ result = subscriptions.add_subscription(handler, [Test1DomainEvent, Test2DomainEvent])
57
+ expect(result).to respond_to(:call)
58
+ end
59
+
60
+ it 'revokes global subscription' do
61
+ handler = TestHandler.new
62
+
63
+ revoke = subscriptions.add_global_subscription(handler)
64
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([handler])
65
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([handler])
66
+ revoke.()
67
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([])
68
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([])
69
+ end
70
+
71
+ it 'revokes subscription' do
72
+ handler = TestHandler.new
73
+
74
+ revoke = subscriptions.add_subscription(handler, [Test1DomainEvent, Test2DomainEvent])
75
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([handler])
76
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([handler])
77
+ revoke.()
78
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([])
79
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([])
80
+ end
81
+
82
+ it 'revokes thread global subscription' do
83
+ handler = TestHandler.new
84
+
85
+ revoke = subscriptions.add_thread_global_subscription(handler)
86
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([handler])
87
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([handler])
88
+ revoke.()
89
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([])
90
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([])
91
+ end
92
+
93
+ it 'revokes thread subscription' do
94
+ handler = TestHandler.new
95
+
96
+ revoke = subscriptions.add_thread_subscription(handler, [Test1DomainEvent, Test2DomainEvent])
97
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([handler])
98
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([handler])
99
+ revoke.()
100
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([])
101
+ expect(subscriptions.all_for('Test2DomainEvent')).to eq([])
102
+ end
103
+
104
+ it 'subscribes by type of event which is a String' do
105
+ handler = TestHandler.new
106
+ subscriptions.add_subscription(handler, ["Test1DomainEvent"])
107
+ subscriptions.add_thread_subscription(handler, ["Test1DomainEvent"])
108
+
109
+ expect(subscriptions.all_for('Test1DomainEvent')).to eq([handler, handler])
110
+ end
111
+ end
@@ -1,85 +1,94 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RubyEventStore
4
+
5
+ # Used for building and executing the query specification.
2
6
  class Specification
3
- NO_LIMIT = Object.new.freeze
4
- NO_BATCH = Object.new.freeze
5
7
  DEFAULT_BATCH_SIZE = 100
6
-
7
- class Result < Struct.new(:direction, :start, :count, :stream, :batch_size)
8
- def limit?
9
- !count.equal?(NO_LIMIT)
10
- end
11
-
12
- def global_stream?
13
- stream.global?
14
- end
15
-
16
- def stream_name
17
- stream.name
18
- end
19
-
20
- def head?
21
- start.equal?(:head)
22
- end
23
-
24
- def forward?
25
- direction.equal?(:forward)
26
- end
27
-
28
- def backward?
29
- !forward?
30
- end
31
-
32
- def batched?
33
- !batch_size.equal?(NO_BATCH)
34
- end
35
- end
36
- private_constant :Result
37
-
38
- attr_reader :result
39
-
40
- def initialize(repository, mapper, result = Result.new(:forward, :head, NO_LIMIT, Stream.new(GLOBAL_STREAM), NO_BATCH))
41
- @mapper = mapper
42
- @repository = repository
8
+ # @api private
9
+ # @private
10
+ def initialize(reader, result = SpecificationResult.new)
11
+ @reader = reader
43
12
  @result = result
44
13
  end
45
14
 
15
+ # Limits the query to certain stream.
16
+ # {http://railseventstore.org/docs/read/ Find out more}.
17
+ #
18
+ # @param stream_name [String] name of the stream to get events from
19
+ # @return [Specification]
46
20
  def stream(stream_name)
47
- Specification.new(repository, mapper, result.dup.tap { |r| r.stream = Stream.new(stream_name) })
21
+ Specification.new(reader, result.dup { |r| r.stream = Stream.new(stream_name) })
48
22
  end
49
23
 
24
+ # Limits the query to events before or after another event.
25
+ # {http://railseventstore.org/docs/read/ Find out more}.
26
+ #
27
+ # @param start [String] id of event to start reading from.
28
+ # @return [Specification]
50
29
  def from(start)
51
- case start
52
- when Symbol
53
- raise InvalidPageStart unless [:head].include?(start)
54
- else
55
- raise InvalidPageStart if start.nil? || start.empty?
56
- raise EventNotFound.new(start) unless repository.has_event?(start)
57
- end
58
- Specification.new(repository, mapper, result.dup.tap { |r| r.start = start })
30
+ raise InvalidPageStart if start.nil? || start.empty?
31
+ raise EventNotFound.new(start) unless reader.has_event?(start)
32
+ Specification.new(reader, result.dup { |r| r.start = start })
33
+ end
34
+
35
+ # Limits the query to events before or after another event.
36
+ # {http://railseventstore.org/docs/read/ Find out more}.
37
+ #
38
+ # @param start [String] id of event to start reading from.
39
+ # @return [Specification]
40
+ def to(stop)
41
+ raise InvalidPageStop if stop.nil? || stop.empty?
42
+ raise EventNotFound.new(stop) unless reader.has_event?(stop)
43
+ Specification.new(reader, result.dup { |r| r.stop = stop })
59
44
  end
60
45
 
46
+ # Sets the order of reading events to ascending (forward from the start).
47
+ # {http://railseventstore.org/docs/read/ Find out more}.
48
+ #
49
+ # @return [Specification]
61
50
  def forward
62
- Specification.new(repository, mapper, result.dup.tap { |r| r.direction = :forward })
51
+ Specification.new(reader, result.dup { |r| r.direction = :forward })
63
52
  end
64
53
 
54
+ # Sets the order of reading events to descending (backward from the start).
55
+ # {http://railseventstore.org/docs/read/ Find out more}.
56
+ #
57
+ # @return [Specification]
65
58
  def backward
66
- Specification.new(repository, mapper, result.dup.tap { |r| r.direction = :backward })
59
+ Specification.new(reader, result.dup { |r| r.direction = :backward })
67
60
  end
68
61
 
62
+ # Limits the query to specified number of events.
63
+ # {http://railseventstore.org/docs/read/ Find out more}.
64
+ #
65
+ # @param count [Integer] maximal number of events to retrieve
66
+ # @return [Specification]
69
67
  def limit(count)
70
68
  raise InvalidPageSize unless count && count > 0
71
- Specification.new(repository, mapper, result.dup.tap { |r| r.count = count })
69
+ Specification.new(reader, result.dup { |r| r.count = count })
72
70
  end
73
71
 
72
+ # Executes the query based on the specification built up to this point.
73
+ # Yields each batch of records that was retrieved from the store.
74
+ # {http://railseventstore.org/docs/read/ Find out more}.
75
+ #
76
+ # @yield [Array<Event, Proto>] batch of events
77
+ # @return [Enumerator, nil] Enumerator is returned when block not given
74
78
  def each_batch
75
79
  return to_enum(:each_batch) unless block_given?
76
80
 
77
- result_ = result.batched? ? result : result.tap { |r| r.batch_size = DEFAULT_BATCH_SIZE }
78
- repository.read(result_).each do |batch|
79
- yield batch.map { |serialized_record| mapper.serialized_record_to_event(serialized_record) }
81
+ reader.each(in_batches(result.batch_size).result) do |batch|
82
+ yield batch
80
83
  end
81
84
  end
82
85
 
86
+ # Executes the query based on the specification built up to this point.
87
+ # Yields events read from the store if block given. Otherwise, returns enumerable collection.
88
+ # {http://railseventstore.org/docs/read/ Find out more}.
89
+ #
90
+ # @yield [Event, Proto] event
91
+ # @return [Enumerator, nil] Enumerator is returned when block not given
83
92
  def each
84
93
  return to_enum unless block_given?
85
94
 
@@ -88,12 +97,148 @@ module RubyEventStore
88
97
  end
89
98
  end
90
99
 
100
+ # Executes the query based on the specification built up to this point
101
+ # and maps the result using provided block.
102
+ # {http://railseventstore.org/docs/read/ Find out more}.
103
+ #
104
+ # @return [Array] of mapped result
105
+ def map(&block)
106
+ raise ArgumentError.new("Block must be given") unless block_given?
107
+ each.map(&block)
108
+ end
109
+
110
+ # Reduces the results of the query based on the specification
111
+ # built up to this point result using provided block.
112
+ # {http://railseventstore.org/docs/read/ Find out more}.
113
+ #
114
+ # @accumulator starting state for reduce operation
115
+ # @return reduce result as defined by block given
116
+ def reduce(accumulator = nil, &block)
117
+ raise ArgumentError.new("Block must be given") unless block_given?
118
+ each.reduce(accumulator, &block)
119
+ end
120
+
121
+ # Calculates the size of result set based on the specification build up to this point.
122
+ # {http://railseventstore.org/docs/read/ Find out more}.
123
+ #
124
+ # @return [Integer] Number of events to read
125
+ def count
126
+ reader.count(result)
127
+ end
128
+
129
+ # Executes the query based on the specification built up to this point.
130
+ # Returns array of domain events.
131
+ # {http://railseventstore.org/docs/read/ Find out more}.
132
+ #
133
+ # @return [Array<Event, Proto>]
134
+ def to_a
135
+ each.to_a
136
+ end
137
+
138
+ # Specifies that events should be obtained in batches.
139
+ # {http://railseventstore.org/docs/read/ Find out more}.
140
+ #
141
+ # Looping through a collection of events from the store
142
+ # can be inefficient since it will try to instantiate all
143
+ # the events at once.
144
+ #
145
+ # In that case, batch processing methods allow you to work
146
+ # with the records in batches, thereby greatly reducing
147
+ # memory consumption.
148
+ #
149
+ # @param batch_size [Integer] number of events to read in a single batch
150
+ # @return [Specification]
91
151
  def in_batches(batch_size = DEFAULT_BATCH_SIZE)
92
- Specification.new(repository, mapper, result.dup.tap { |r| r.batch_size = batch_size })
152
+ Specification.new(reader, result.dup { |r| r.read_as = :batch; r.batch_size = batch_size })
93
153
  end
94
154
  alias :in_batches_of :in_batches
95
155
 
156
+ # Specifies that only first event should be read.
157
+ # {http://railseventstore.org/docs/read/ Find out more}.
158
+ #
159
+ # @return [Specification]
160
+ def read_first
161
+ Specification.new(reader, result.dup { |r| r.read_as = :first })
162
+ end
163
+
164
+ # Specifies that only last event should be read.
165
+ # {http://railseventstore.org/docs/read/ Find out more}.
166
+ #
167
+ # @return [Specification]
168
+ def read_last
169
+ Specification.new(reader, result.dup { |r| r.read_as = :last })
170
+ end
171
+
172
+ # Executes the query based on the specification built up to this point.
173
+ # Returns the first event in specified collection of events.
174
+ # {http://railseventstore.org/docs/read/ Find out more}.
175
+ #
176
+ # @return [Event, nil]
177
+ def first
178
+ reader.one(read_first.result)
179
+ end
180
+
181
+ # Executes the query based on the specification built up to this point.
182
+ # Returns the last event in specified collection of events.
183
+ # {http://railseventstore.org/docs/read/ Find out more}.
184
+ #
185
+ # @return [Event, nil]
186
+ def last
187
+ reader.one(read_last.result)
188
+ end
189
+
190
+ # Limits the query to certain event type(s).
191
+ # {http://railseventstore.org/docs/read/ Find out more}.
192
+ #
193
+ # @types [Class, Array(Class)] types of event to look for.
194
+ # @return [Specification]
195
+ def of_type(*types)
196
+ Specification.new(reader, result.dup{ |r| r.with_types = types.flatten })
197
+ end
198
+ alias_method :of_types, :of_type
199
+
200
+ # Limits the query to certain events by given even ids.
201
+ # {http://railseventstore.org/docs/read/ Find out more}.
202
+ #
203
+ # @param even_ids [Array(String)] ids of event to look for.
204
+ # @return [Specification]
205
+ def with_id(event_ids)
206
+ Specification.new(reader, result.dup{ |r| r.with_ids = event_ids })
207
+ end
208
+
209
+ # Reads single event from repository.
210
+ # Returns the event with specified id or nil if event is not found
211
+ # in specified collection of events.
212
+ # {http://railseventstore.org/docs/read/ Find out more}.
213
+ #
214
+ # @return [Event, nil]
215
+ def event(event_id)
216
+ reader.one(read_first.with_id([event_id]).result)
217
+ end
218
+
219
+ # Reads single existing event from repository.
220
+ # Returns the event with specified id or raises [EventNotFound] error if
221
+ # event is not found in specified collection of events.
222
+ # {http://railseventstore.org/docs/read/ Find out more}.
223
+ #
224
+ # @return [Event]
225
+ def event!(event_id)
226
+ event(event_id) or raise EventNotFound.new(event_id)
227
+ end
228
+
229
+ # Reads all events of given ids from repository.
230
+ # Yields each event (found by id in specified collection of events)
231
+ # read from the store if block given. Otherwise, returns enumerable collection.
232
+ # {http://railseventstore.org/docs/read/ Find out more}.
233
+ #
234
+ # @yield [Event, Proto] event
235
+ # @return [Enumerator] Enumerator is returned when block not given
236
+ def events(event_ids)
237
+ with_id(event_ids).each
238
+ end
239
+
240
+ attr_reader :result
96
241
  private
97
- attr_reader :repository, :mapper
242
+ attr_reader :reader
98
243
  end
99
244
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ # Used for fetching events based on given query specification.
5
+ class SpecificationReader
6
+ # @api private
7
+ # @private
8
+ def initialize(repository, mapper)
9
+ @repository = repository
10
+ @mapper = mapper
11
+ end
12
+
13
+ # @api private
14
+ # @private
15
+ def one(specification_result)
16
+ record = repository.read(specification_result)
17
+ mapper.serialized_record_to_event(record) if record
18
+ end
19
+
20
+ # @api private
21
+ # @private
22
+ def each(specification_result)
23
+ repository.read(specification_result).each do |batch|
24
+ yield batch.map { |serialized_record| mapper.serialized_record_to_event(serialized_record) }
25
+ end
26
+ end
27
+
28
+ # @api private
29
+ # @private
30
+ def count(specification_result)
31
+ repository.count(specification_result)
32
+ end
33
+
34
+ # @api private
35
+ # @private
36
+ def has_event?(event_id)
37
+ repository.has_event?(event_id)
38
+ end
39
+
40
+ private
41
+ attr_reader :repository, :mapper
42
+ end
43
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ class SpecificationResult
5
+ def initialize(direction: :forward,
6
+ start: nil,
7
+ stop: nil,
8
+ count: nil,
9
+ stream: Stream.new(GLOBAL_STREAM),
10
+ read_as: :all,
11
+ batch_size: Specification::DEFAULT_BATCH_SIZE,
12
+ with_ids: nil,
13
+ with_types: nil)
14
+ @attributes = Struct.new(:direction, :start, :stop, :count, :stream, :read_as, :batch_size, :with_ids, :with_types)
15
+ .new(direction, start, stop, count, stream, read_as, batch_size, with_ids, with_types)
16
+ freeze
17
+ end
18
+
19
+ # Limited results. True if number of read elements are limited
20
+ # {http://railseventstore.org/docs/read/ Find out more}.
21
+ #
22
+ # @return [Boolean]
23
+ def limit?
24
+ !attributes.count.nil?
25
+ end
26
+
27
+ # Results limit or infinity if limit not defined
28
+ # {http://railseventstore.org/docs/read/ Find out more}.
29
+ #
30
+ # @return [Integer|Infinity]
31
+ def limit
32
+ attributes.count || Float::INFINITY
33
+ end
34
+
35
+ # Stream definition. Stream to be read or nil
36
+ # {http://railseventstore.org/docs/read/ Find out more}.
37
+ #
38
+ # @return [Stream|nil]
39
+ def stream
40
+ attributes.stream
41
+ end
42
+
43
+ # Starting position. Event id of starting event
44
+ # {http://railseventstore.org/docs/read/ Find out more}.
45
+ #
46
+ # @return [String]
47
+ def start
48
+ attributes.start
49
+ end
50
+
51
+ # Stop position. Event id of stopping event
52
+ # {http://railseventstore.org/docs/read/ Find out more}.
53
+ #
54
+ # @return [String|Symbol]
55
+ def stop
56
+ attributes.stop
57
+ end
58
+
59
+ # Read direction. True is reading forward
60
+ # {http://railseventstore.org/docs/read/ Find out more}.
61
+ #
62
+ # @return [Boolean]
63
+ def forward?
64
+ get_direction.equal?(:forward)
65
+ end
66
+
67
+ # Read direction. True is reading backward
68
+ # {http://railseventstore.org/docs/read/ Find out more}.
69
+ #
70
+ # @return [Boolean]
71
+ def backward?
72
+ get_direction.equal?(:backward)
73
+ end
74
+
75
+ # Size of batch to read (only for :batch read strategy)
76
+ # {http://railseventstore.org/docs/read/ Find out more}.
77
+ #
78
+ # @return [Integer]
79
+ def batch_size
80
+ attributes.batch_size
81
+ end
82
+
83
+ # Ids of specified event to be read (if any given)
84
+ # {http://railseventstore.org/docs/read/ Find out more}.
85
+ #
86
+ # @return [Array|nil]
87
+ def with_ids
88
+ attributes.with_ids
89
+ end
90
+
91
+ # Read by specified ids. True if event ids have been specified.
92
+ # {http://railseventstore.org/docs/read/ Find out more}.
93
+ #
94
+ # @return [Boolean]
95
+ def with_ids?
96
+ !with_ids.nil?
97
+ end
98
+
99
+ # Event types to be read (if any given)
100
+ # {http://railseventstore.org/docs/read/ Find out more}.
101
+ #
102
+ # @return [Array|nil]
103
+ def with_types
104
+ attributes.with_types&.map(&:to_s)
105
+ end
106
+
107
+ # Read by specified event types. True if event types have been specified.
108
+ # {http://railseventstore.org/docs/read/ Find out more}.
109
+ #
110
+ # @return [Boolean]
111
+ def with_types?
112
+ !(with_types || []).empty?
113
+ end
114
+
115
+ # Read strategy. True if items will be read in batches
116
+ # {http://railseventstore.org/docs/read/ Find out more}.
117
+ #
118
+ # @return [Boolean]
119
+ def batched?
120
+ attributes.read_as.equal?(:batch)
121
+ end
122
+
123
+ # Read strategy. True if first item will be read
124
+ # {http://railseventstore.org/docs/read/ Find out more}.
125
+ #
126
+ # @return [Boolean]
127
+ def first?
128
+ attributes.read_as.equal?(:first)
129
+ end
130
+
131
+ # Read strategy. True if last item will be read
132
+ # {http://railseventstore.org/docs/read/ Find out more}.
133
+ #
134
+ # @return [Boolean]
135
+ def last?
136
+ attributes.read_as.equal?(:last)
137
+ end
138
+
139
+ # Read strategy. True if all items will be read
140
+ # {http://railseventstore.org/docs/read/ Find out more}.
141
+ #
142
+ # @return [Boolean]
143
+ def all?
144
+ attributes.read_as.equal?(:all)
145
+ end
146
+
147
+ # Clone [SpecificationResult]
148
+ # If block is given cloned attributes might be modified.
149
+ #
150
+ # @return [SpecificationResult]
151
+ def dup
152
+ new_attributes = attributes.dup
153
+ yield new_attributes if block_given?
154
+ SpecificationResult.new(**new_attributes.to_h)
155
+ end
156
+
157
+ # Two specification attributess are equal if:
158
+ # * they are of the same class
159
+ # * have identical data (verified with eql? method)
160
+ #
161
+ # @param other_spec [SpecificationResult, Object] object to compare
162
+ #
163
+ # @return [TrueClass, FalseClass]
164
+ def ==(other_spec)
165
+ other_spec.hash.eql?(hash)
166
+ end
167
+
168
+ # @private
169
+ BIG_VALUE = 0b100010010100011110111101100001011111100101001010111110101000000
170
+
171
+ # Generates a Fixnum hash value for this object. This function
172
+ # have the property that a.eql?(b) implies a.hash == b.hash.
173
+ #
174
+ # The hash value is used along with eql? by the Hash class to
175
+ # determine if two objects reference the same hash key.
176
+ #
177
+ # This hash is based on
178
+ # * class
179
+ # * direction
180
+ # * start
181
+ # * stop
182
+ # * count
183
+ # * stream
184
+ # * read_as
185
+ # * batch_size
186
+ # * with_ids
187
+ # * with_types
188
+ #
189
+ # @return [Integer]
190
+ def hash
191
+ [
192
+ self.class,
193
+ get_direction,
194
+ start,
195
+ stop,
196
+ limit,
197
+ stream,
198
+ attributes.read_as,
199
+ batch_size,
200
+ with_ids,
201
+ with_types,
202
+ ].hash ^ BIG_VALUE
203
+ end
204
+
205
+ private
206
+ attr_reader :attributes
207
+
208
+ def get_direction
209
+ attributes.direction
210
+ end
211
+ end
212
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RubyEventStore
2
4
  class Stream
3
5
  def initialize(name)