pg_eventstore 1.11.0 → 1.12.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/docs/reading_events.md +98 -0
  4. data/lib/pg_eventstore/client.rb +22 -4
  5. data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +69 -0
  6. data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +31 -0
  7. data/lib/pg_eventstore/commands.rb +2 -0
  8. data/lib/pg_eventstore/partition.rb +23 -0
  9. data/lib/pg_eventstore/queries/event_queries.rb +18 -0
  10. data/lib/pg_eventstore/queries/partition_queries.rb +21 -0
  11. data/lib/pg_eventstore/queries.rb +2 -0
  12. data/lib/pg_eventstore/query_builders/basic_filtering.rb +27 -0
  13. data/lib/pg_eventstore/query_builders/events_filtering.rb +47 -31
  14. data/lib/pg_eventstore/query_builders/partitions_filtering.rb +83 -0
  15. data/lib/pg_eventstore/sql_builder.rb +10 -0
  16. data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +1 -9
  17. data/lib/pg_eventstore/utils.rb +5 -4
  18. data/lib/pg_eventstore/version.rb +1 -1
  19. data/lib/pg_eventstore/web/application.rb +4 -3
  20. data/lib/pg_eventstore.rb +1 -0
  21. data/sig/pg_eventstore/client.rbs +2 -0
  22. data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +16 -0
  23. data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +8 -0
  24. data/sig/pg_eventstore/partition.rbs +15 -0
  25. data/sig/pg_eventstore/queries/event_queries.rbs +2 -0
  26. data/sig/pg_eventstore/queries/partition_queries.rbs +6 -0
  27. data/sig/pg_eventstore/query_builders/basic_filtering.rbs +15 -0
  28. data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +17 -17
  29. data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +21 -0
  30. data/sig/pg_eventstore/sql_builder.rbs +1 -1
  31. metadata +13 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 402dc90a012b5e1ee934da4f62c4af4ba62e69693a45dac1a84d461950da5717
4
- data.tar.gz: 6c5d1fdc2e9475eca72b5ef27d0081e0aec6331f76639b7172e676d8b6e63e3d
3
+ metadata.gz: e0de14f58d080bcab32005e045530ca7888761e5415d013dbb262746a02806ba
4
+ data.tar.gz: fee7b7335212796361b5ded87497c7d744ed5ddfd011a47d54cc2b545991b227
5
5
  SHA512:
6
- metadata.gz: b20c1124f2108b671bd28f3677fb3b2ae88f3b19d2470eb5a31102eb597d4bd5af1080f9bd337b1528107eef2e4bce6b882d434aa8536abc0a5a80b373bfe3a3
7
- data.tar.gz: 9fd5af079cedc277e278b2e0b50e628601880a81ec5bf7c94700595f6472be389b2fa3d3a2ba2f5d311c8844da40de2c4a52e4dc6c2bda2ca3559e94b267f7f2
6
+ metadata.gz: 0dbdd04e5778b292177062e5a44ed9ee4c2fc03e98da945a73523036f62ed09cc27ac00d1a5853e99975b82fc798f2f83639bbb2cd4f1745150ff121ca5d7056
7
+ data.tar.gz: 060f8b1b51cda7be2c472f914dc611fee2db052a2cd773676013b2175e58cf12c3895e8b631a0d79443804f3a733ca25d7012fac6519551308c890df065457d5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.12.0]
4
+
5
+ - Introduce `#read_grouped` API method that allows to group events by type
6
+
3
7
  ## [1.11.0]
4
8
 
5
9
  - Add a global position that caused an error to the subscription's error JSON info. This will help you understand what event caused your subscription to fail.
@@ -230,3 +230,101 @@ PgEventstore.client.read_paginated(projection_stream, options: { resolve_link_to
230
230
  end
231
231
  end
232
232
  ```
233
+
234
+ ## Grouping events by type
235
+
236
+ `pg_eventstore` implements an ability to group events by type when reading from a stream. Example:
237
+
238
+ ```ruby
239
+ stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
240
+ event1 = PgEventstore::Event.new(type: 'Foo', data: { foo: 1 })
241
+ event2 = PgEventstore::Event.new(type: 'Foo', data: { foo: 2 })
242
+ event3 = PgEventstore::Event.new(type: 'Bar', data: { bar: 2 })
243
+
244
+ PgEventstore.client.append_to_stream(stream, [event1, event2, event3])
245
+
246
+ PgEventstore.client.read_grouped(stream) # => returns event1 and event3
247
+ ```
248
+
249
+ API is very similar to the API of `#read`, but it ignores `:max_count` options as the result is always returns a set of event types in your stream.
250
+
251
+ Reading most recent events:
252
+
253
+ ```ruby
254
+ PgEventstore.client.read_grouped(stream, options: { direction: :desc })
255
+ ```
256
+
257
+ Filtering the result by stream attributes:
258
+
259
+ ```ruby
260
+ PgEventstore.client.read_grouped(
261
+ PgEventstore::Stream.all_stream,
262
+ options: { filter: { streams: [{ context: 'FooCtx' }] } }
263
+ )
264
+ ```
265
+
266
+ Filtering the result by event types:
267
+
268
+ ```ruby
269
+ PgEventstore.client.read_grouped(
270
+ PgEventstore::Stream.all_stream,
271
+ options: { filter: { event_types: ['Foo', 'Bar'] } }
272
+ )
273
+ ```
274
+
275
+ Filtering by stream attributes and event types:
276
+
277
+ ```ruby
278
+ PgEventstore.client.read_grouped(
279
+ PgEventstore::Stream.all_stream,
280
+ options: { filter: { streams: [{ context: 'FooCtx' }], event_types: ['Foo', 'Bar'] } }
281
+ )
282
+ ```
283
+
284
+ Reading most recent events until the certain stream revision:
285
+
286
+ ```ruby
287
+ PgEventstore.client.read_grouped(stream, options: { direction: :desc, from_revision: 1 })
288
+ ```
289
+
290
+ Reading the oldest events from the certain stream revision:
291
+
292
+ ```ruby
293
+ PgEventstore.client.read_grouped(stream, options: { direction: :asc, from_revision: 1 })
294
+ ```
295
+
296
+ Reading most recent events until the certain global position:
297
+
298
+ ```ruby
299
+ PgEventstore.client.read_grouped(PgEventstore::Stream.all_stream, options: { direction: :desc, from_position: 5 })
300
+ ```
301
+
302
+ Reading the oldest events from the certain global position:
303
+
304
+ ```ruby
305
+ PgEventstore.client.read_grouped(PgEventstore::Stream.all_stream, options: { direction: :asc, from_position: 5 })
306
+ ```
307
+
308
+ ### Event types list lookup
309
+
310
+ If you do not provide event types filter - event types list will be determined based on the rest of arguments(a stream argument or a stream filters option).
311
+
312
+ ### Multiple events of same type in the result
313
+
314
+ If same event type appear in different streams(different by `#context` and `#stream_name`) - those events will appear in the result. This is because even though `Event#type` value may be the same - its meaning may have different meaning in different `context`/`stream_name` couple. Example:
315
+
316
+ ```ruby
317
+ stream1 = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Foo', stream_id: '1')
318
+ stream2 = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'Bar', stream_id: '1')
319
+
320
+ event1 = PgEventstore::Event.new(type: 'Foo', data: { foo: 1 })
321
+ event2 = PgEventstore::Event.new(type: 'Foo', data: { foo: 2 })
322
+
323
+ PgEventstore.client.append_to_stream(stream1, event1)
324
+ PgEventstore.client.append_to_stream(stream2, event2)
325
+
326
+ PgEventstore.client.read_grouped(
327
+ PgEventstore::Stream.all_stream,
328
+ options: { filter: { streams: [{ context: 'FooCtx' }] } }
329
+ ) # => returns both events even though they are of "Foo" type
330
+ ```
@@ -55,10 +55,11 @@ module PgEventstore
55
55
  # Read events from the specific stream or from "all" stream.
56
56
  # @param stream [PgEventstore::Stream]
57
57
  # @param options [Hash] request options
58
- # @option options [String] :direction read direction - 'Forwards' or 'Backwards'
58
+ # @option options [String] :direction read direction. Allowed values are "Forwards", "Backwards", "asc", "desc",
59
+ # :asc, :desc
59
60
  # @option options [Integer] :from_revision a starting revision number. **Use this option when stream name is a
60
61
  # normal stream name**
61
- # @option options [Integer, Symbol] :from_position a starting global position number. **Use this option when reading
62
+ # @option options [Integer] :from_position a starting global position number. **Use this option when reading
62
63
  # from "all" stream**
63
64
  # @option options [Integer] :max_count max number of events to return in one response. Defaults to config.max_count
64
65
  # @option options [Boolean] :resolve_link_tos When using projections to create new events you
@@ -94,7 +95,7 @@ module PgEventstore
94
95
  # }
95
96
  # )
96
97
  #
97
- # # Filtering the a mix of context and event type
98
+ # # Filtering a mix of context and event type
98
99
  # PgEventstore.client.read(
99
100
  # PgEventstore::Stream.all_stream,
100
101
  # options: { filter: { streams: [{ context: 'User' }], event_types: ['MyAwesomeEvent'] } }
@@ -123,6 +124,23 @@ module PgEventstore
123
124
  call(stream, options: { max_count: config.max_count }.merge(options))
124
125
  end
125
126
 
127
+ # Takes a stream, determines a list of even types in it and returns most recent(or very first - depending on
128
+ # :direction option) events, one of each type. If :event_types filter is provided - uses it instead of automatic
129
+ # event types lookup logic. The result size is almost always less than or equal to event types list size, so passing
130
+ # :max_count option does not make any effect. In case if event of same type appears in different context/stream
131
+ # name - it will be counted as a different event, thus, may appear several times in the result.
132
+ # @see {#read} for the detailed docs
133
+ # @param stream [PgEventstore::Stream]
134
+ # @param options [Hash] request options
135
+ # @param middlewares [Array, nil]
136
+ # @return [Array<PgEventstore::Event>]
137
+ def read_grouped(stream, options: {}, middlewares: nil)
138
+ cmd_class = stream.all_stream? ? Commands::AllStreamReadGrouped : Commands::RegularStreamReadGrouped
139
+ cmd_class.
140
+ new(Queries.new(partitions: partition_queries, events: event_queries(middlewares(middlewares)))).
141
+ call(stream, options: options)
142
+ end
143
+
126
144
  # Links event from one stream into another stream. You can later access it by providing :resolve_link_tos option
127
145
  # when reading from a stream. Only existing events can be linked.
128
146
  # @param stream [PgEventstore::Stream]
@@ -149,7 +167,7 @@ module PgEventstore
149
167
  private
150
168
 
151
169
  # @param middlewares [Array, nil]
152
- # @return [Array<Object<#serialize, #deserialize>>]
170
+ # @return [Array<PgEventstore::Middleware>]
153
171
  def middlewares(middlewares = nil)
154
172
  return config.middlewares.values unless middlewares
155
173
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module Commands
5
+ # @!visibility private
6
+ class AllStreamReadGrouped < AbstractCommand
7
+ # @param stream [PgEventstore::Stream]
8
+ # @param options [Hash] request options
9
+ # @option options [String] :direction read direction
10
+ # @option options [Integer, Symbol] :from_position. **Use this option when reading from "all" stream**
11
+ # @option options [Boolean] :resolve_link_tos
12
+ # @option options [Hash] :filter provide it to filter events
13
+ # @return [Array<PgEventstore::Event>]
14
+ # @raise [PgEventstore::StreamNotFoundError]
15
+ def call(stream, options: {})
16
+ event_types = QueryBuilders::PartitionsFiltering.extract_event_types_filter(options)
17
+ stream_filters = QueryBuilders::PartitionsFiltering.extract_streams_filter(options)
18
+ stream_ids_grouped = group_stream_ids(options)
19
+ options_by_event_type =
20
+ queries.partitions.partitions(stream_filters, event_types).flat_map do |partition|
21
+ stream_ids = stream_ids_grouped[[partition.context, partition.stream_name]]
22
+ next build_filter_options_for_streams(partition, stream_ids, options) if stream_ids
23
+
24
+ build_filter_options_for_partitions(partition, options)
25
+ end
26
+ queries.events.grouped_events(stream, options_by_event_type, **options.slice(:resolve_link_tos))
27
+ end
28
+
29
+ private
30
+
31
+ # @param options [Hash]
32
+ # @return [Hash]
33
+ def group_stream_ids(options)
34
+ event_stream_filters = QueryBuilders::EventsFiltering.extract_streams_filter(options)
35
+ event_stream_filters.each_with_object({}) do |attrs, res|
36
+ next unless attrs[:stream_id]
37
+
38
+ res[[attrs[:context], attrs[:stream_name]]] ||= []
39
+ res[[attrs[:context], attrs[:stream_name]]].push(attrs[:stream_id])
40
+ end
41
+ end
42
+
43
+ # @param partition [PgEventstore::Partition]
44
+ # @param stream_ids [Array<String>]
45
+ # @param options [Hash]
46
+ # @return [Array<Hash>]
47
+ def build_filter_options_for_streams(partition, stream_ids, options)
48
+ stream_ids.map do |stream_id|
49
+ filter = {
50
+ streams: [{ context: partition.context, stream_name: partition.stream_name, stream_id: stream_id }],
51
+ event_types: [partition.event_type]
52
+ }
53
+ options.merge(filter: filter, max_count: 1)
54
+ end
55
+ end
56
+
57
+ # @param partition [PgEventstore::Partition]
58
+ # @param options [Hash]
59
+ # @return [Hash]
60
+ def build_filter_options_for_partitions(partition, options)
61
+ filter = {
62
+ streams: [{ context: partition.context, stream_name: partition.stream_name }],
63
+ event_types: [partition.event_type]
64
+ }
65
+ options.merge(filter: filter, max_count: 1)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module Commands
5
+ # @!visibility private
6
+ class RegularStreamReadGrouped < AbstractCommand
7
+ # @param stream [PgEventstore::Stream]
8
+ # @param options [Hash] request options
9
+ # @option options [String] :direction read direction
10
+ # @option options [Integer, Symbol] :from_revision. **Use this option when stream name is a normal stream name**
11
+ # @option options [Integer, Symbol] :from_position. **Use this option when reading from "all" stream**
12
+ # @option options [Boolean] :resolve_link_tos
13
+ # @option options [Hash] :filter provide it to filter events
14
+ # @return [Array<PgEventstore::Event>]
15
+ # @raise [PgEventstore::StreamNotFoundError]
16
+ def call(stream, options: {})
17
+ queries.events.stream_revision(stream) || raise(StreamNotFoundError, stream)
18
+
19
+ event_types = QueryBuilders::PartitionsFiltering.extract_event_types_filter(options)
20
+ stream_filters = QueryBuilders::PartitionsFiltering.extract_streams_filter(
21
+ filter: { streams: [{ context: stream.context, stream_name: stream.stream_name }] }
22
+ )
23
+ options_by_event_type = queries.partitions.partitions(stream_filters, event_types).map do |partition|
24
+ filter = { event_types: [partition.event_type] }
25
+ options.merge(filter: filter, max_count: 1)
26
+ end
27
+ queries.events.grouped_events(stream, options_by_event_type, **options.slice(:resolve_link_tos))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -5,6 +5,8 @@ require_relative 'commands/event_modifiers/prepare_link_event'
5
5
  require_relative 'commands/event_modifiers/prepare_regular_event'
6
6
  require_relative 'commands/append'
7
7
  require_relative 'commands/read'
8
+ require_relative 'commands/regular_stream_read_grouped'
9
+ require_relative 'commands/all_stream_read_grouped'
8
10
  require_relative 'commands/regular_stream_read_paginated'
9
11
  require_relative 'commands/system_stream_read_paginated'
10
12
  require_relative 'commands/multiple'
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ class Partition
5
+ include Extensions::OptionsExtension
6
+
7
+ # @!attribute id
8
+ # @return [Integer]
9
+ option(:id)
10
+ # @!attribute context
11
+ # @return [String]
12
+ option(:context)
13
+ # @!attribute stream_name
14
+ # @return [String, nil]
15
+ option(:stream_name)
16
+ # @!attribute event_type
17
+ # @return [String, nil]
18
+ option(:event_type)
19
+ # @!attribute table_name
20
+ # @return [String]
21
+ option(:table_name)
22
+ end
23
+ end
@@ -98,6 +98,24 @@ module PgEventstore
98
98
  end
99
99
  end
100
100
 
101
+ # @param stream [PgEventstore::Stream]
102
+ # @param options_by_event_type [Array<Hash>] a set of options per an event type
103
+ # @param options [Hash]
104
+ # @option options [Boolean] :resolve_link_tos
105
+ # @return [Array<PgEventstore::Event>]
106
+ def grouped_events(stream, options_by_event_type, **options)
107
+ builders = options_by_event_type.map do |filter|
108
+ QueryBuilders::EventsFiltering.events_filtering(stream, filter)
109
+ end
110
+ final_builder = SQLBuilder.union_builders(builders.map(&:to_sql_builder))
111
+
112
+ raw_events = connection.with do |conn|
113
+ conn.exec_params(*final_builder.to_exec_params)
114
+ end.to_a
115
+ raw_events = links_resolver.resolve(raw_events) if options[:resolve_link_tos]
116
+ deserializer.deserialize_many(raw_events)
117
+ end
118
+
101
119
  private
102
120
 
103
121
  # @param stream [PgEventstore::Stream]
@@ -176,6 +176,19 @@ module PgEventstore
176
176
  end.to_a
177
177
  end
178
178
 
179
+ # @param stream_filters [Array<Hash[Symbol, String]>]
180
+ # @param event_filters [Array<String>]
181
+ # @return [Array<PgEventstore::Partition>]
182
+ def partitions(stream_filters, event_filters)
183
+ partitions_filter = QueryBuilders::PartitionsFiltering.new
184
+ stream_filters.each { |attrs| partitions_filter.add_stream_attrs(**attrs) }
185
+ partitions_filter.add_event_types(event_filters)
186
+ partitions_filter.with_event_types
187
+ connection.with do |conn|
188
+ conn.exec_params(*partitions_filter.to_exec_params)
189
+ end.map(&method(:deserialize))
190
+ end
191
+
179
192
  # @param stream [PgEventstore::Stream]
180
193
  # @return [String]
181
194
  def context_partition_name(stream)
@@ -194,5 +207,13 @@ module PgEventstore
194
207
  def event_type_partition_name(stream, event_type)
195
208
  "event_types_#{Digest::MD5.hexdigest("#{stream.context}-#{stream.stream_name}-#{event_type}")[0..5]}"
196
209
  end
210
+
211
+ private
212
+
213
+ # @param attrs [Hash]
214
+ # @return [PgEventstore::Partition]
215
+ def deserialize(attrs)
216
+ Partition.new(**attrs.transform_keys(&:to_sym))
217
+ end
197
218
  end
198
219
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'sql_builder'
4
+ require_relative 'query_builders/basic_filtering'
4
5
  require_relative 'query_builders/events_filtering'
6
+ require_relative 'query_builders/partitions_filtering'
5
7
  require_relative 'queries/transaction_queries'
6
8
  require_relative 'queries/event_queries'
7
9
  require_relative 'queries/partition_queries'
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ # @!visibility private
6
+ class BasicFiltering
7
+ def initialize
8
+ @sql_builder = SQLBuilder.new.select("#{to_table_name}.*").from(to_table_name)
9
+ end
10
+
11
+ # @return [String]
12
+ def to_table_name
13
+ raise NotImplementedError
14
+ end
15
+
16
+ # @return [PgEventstore::SQLBuilder]
17
+ def to_sql_builder
18
+ @sql_builder
19
+ end
20
+
21
+ # @return [Array]
22
+ def to_exec_params
23
+ @sql_builder.to_exec_params
24
+ end
25
+ end
26
+ end
27
+ end
@@ -3,7 +3,11 @@
3
3
  module PgEventstore
4
4
  module QueryBuilders
5
5
  # @!visibility private
6
- class EventsFiltering
6
+ class EventsFiltering < BasicFiltering
7
+ # @return [String]
8
+ TABLE_NAME = 'events'
9
+ private_constant :TABLE_NAME
10
+
7
11
  # @return [Integer]
8
12
  DEFAULT_LIMIT = 1_000
9
13
  # @return [Hash<String => String, Symbol => String>]
@@ -26,6 +30,7 @@ module PgEventstore
26
30
  # @return [PgEventstore::EventsFiltering]
27
31
  def events_filtering(stream, options)
28
32
  return all_stream_filtering(options) if stream.all_stream?
33
+
29
34
  if stream.system? && Stream::KNOWN_SYSTEM_STREAMS.include?(stream.context)
30
35
  return system_stream_filtering(stream, options)
31
36
  end
@@ -43,11 +48,9 @@ module PgEventstore
43
48
  # @return [PgEventstore::QueryBuilders::EventsFiltering]
44
49
  def all_stream_filtering(options)
45
50
  event_filter = new
46
- options in { filter: { event_types: Array => event_types } }
47
- event_filter.add_event_types(event_types)
51
+ event_filter.add_event_types(extract_event_types_filter(options))
48
52
  event_filter.add_limit(options[:max_count])
49
- options in { filter: { streams: Array => streams } }
50
- streams&.each { |attrs| event_filter.add_stream_attrs(**attrs) }
53
+ extract_streams_filter(options).each { |attrs| event_filter.add_stream_attrs(**attrs) }
51
54
  event_filter.add_global_position(options[:from_position], options[:direction])
52
55
  event_filter.add_all_stream_direction(options[:direction])
53
56
  event_filter
@@ -58,8 +61,7 @@ module PgEventstore
58
61
  # @return [PgEventstore::QueryBuilders::EventsFiltering]
59
62
  def specific_stream_filtering(stream, options)
60
63
  event_filter = new
61
- options in { filter: { event_types: Array => event_types } }
62
- event_filter.add_event_types(event_types)
64
+ event_filter.add_event_types(extract_event_types_filter(options))
63
65
  event_filter.add_limit(options[:max_count])
64
66
  event_filter.add_stream_attrs(**stream.to_hash)
65
67
  event_filter.add_revision(options[:from_revision], options[:direction])
@@ -75,14 +77,39 @@ module PgEventstore
75
77
  event_filter.set_source(stream.context)
76
78
  end
77
79
  end
80
+
81
+ # @param options [Hash]
82
+ # @return [Array<String>]
83
+ def extract_event_types_filter(options)
84
+ options in { filter: { event_types: Array => event_types } }
85
+ event_types&.select! do
86
+ _1.is_a?(String)
87
+ end
88
+ event_types || []
89
+ end
90
+
91
+ # @param options [Hash]
92
+ # @return [Array<Hash[Symbol, String]>]
93
+ def extract_streams_filter(options)
94
+ options in { filter: { streams: Array => streams } }
95
+ streams = streams&.map do
96
+ _1 in { context: String | NilClass => context }
97
+ _1 in { stream_name: String | NilClass => stream_name }
98
+ _1 in { stream_id: String | NilClass => stream_id }
99
+ { context: context, stream_name: stream_name, stream_id: stream_id }
100
+ end
101
+ streams || []
102
+ end
78
103
  end
79
104
 
80
105
  def initialize
81
- @sql_builder =
82
- SQLBuilder.new.
83
- select('events.*').
84
- from('events').
85
- limit(DEFAULT_LIMIT)
106
+ super
107
+ @sql_builder.limit(DEFAULT_LIMIT)
108
+ end
109
+
110
+ # @return [String]
111
+ def to_table_name
112
+ TABLE_NAME
86
113
  end
87
114
 
88
115
  # @param context [String, nil]
@@ -95,21 +122,20 @@ module PgEventstore
95
122
 
96
123
  stream_attrs.compact!
97
124
  sql = stream_attrs.map do |attr, _|
98
- "events.#{attr} = ?"
125
+ "#{to_table_name}.#{attr} = ?"
99
126
  end.join(" AND ")
100
127
  @sql_builder.where_or(sql, *stream_attrs.values)
101
128
  end
102
129
 
103
- # @param event_types [Array<String>, nil]
130
+ # @param event_types [Array<String>]
104
131
  # @return [void]
105
132
  def add_event_types(event_types)
106
- return if event_types.nil?
107
133
  return if event_types.empty?
108
134
 
109
135
  sql = event_types.size.times.map do
110
136
  "?"
111
137
  end.join(", ")
112
- @sql_builder.where("events.type IN (#{sql})", *event_types)
138
+ @sql_builder.where("#{to_table_name}.type IN (#{sql})", *event_types)
113
139
  end
114
140
 
115
141
  # @param revision [Integer, nil]
@@ -118,7 +144,7 @@ module PgEventstore
118
144
  def add_revision(revision, direction)
119
145
  return unless revision
120
146
 
121
- @sql_builder.where("events.stream_revision #{direction_operator(direction)} ?", revision)
147
+ @sql_builder.where("#{to_table_name}.stream_revision #{direction_operator(direction)} ?", revision)
122
148
  end
123
149
 
124
150
  # @param position [Integer, nil]
@@ -127,19 +153,19 @@ module PgEventstore
127
153
  def add_global_position(position, direction)
128
154
  return unless position
129
155
 
130
- @sql_builder.where("events.global_position #{direction_operator(direction)} ?", position)
156
+ @sql_builder.where("#{to_table_name}.global_position #{direction_operator(direction)} ?", position)
131
157
  end
132
158
 
133
159
  # @param direction [String, Symbol, nil]
134
160
  # @return [void]
135
161
  def add_stream_direction(direction)
136
- @sql_builder.order("events.stream_revision #{SQL_DIRECTIONS[direction]}")
162
+ @sql_builder.order("#{to_table_name}.stream_revision #{SQL_DIRECTIONS[direction]}")
137
163
  end
138
164
 
139
165
  # @param direction [String, Symbol, nil]
140
166
  # @return [void]
141
167
  def add_all_stream_direction(direction)
142
- @sql_builder.order("events.global_position #{SQL_DIRECTIONS[direction]}")
168
+ @sql_builder.order("#{to_table_name}.global_position #{SQL_DIRECTIONS[direction]}")
143
169
  end
144
170
 
145
171
  # @param limit [Integer, nil]
@@ -150,20 +176,10 @@ module PgEventstore
150
176
  @sql_builder.limit(limit)
151
177
  end
152
178
 
153
- # @return [PgEventstore::SQLBuilder]
154
- def to_sql_builder
155
- @sql_builder
156
- end
157
-
158
- # @return [Array]
159
- def to_exec_params
160
- @sql_builder.to_exec_params
161
- end
162
-
163
179
  # @param table_name [String] system stream view name
164
180
  # @return [void]
165
181
  def set_source(table_name)
166
- @sql_builder.from(%{ "#{PG::Connection.escape(table_name)}" events })
182
+ @sql_builder.from(%{ "#{PG::Connection.escape(table_name)}" #{to_table_name} })
167
183
  end
168
184
 
169
185
  private
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module QueryBuilders
5
+ # @!visibility private
6
+ class PartitionsFiltering < BasicFiltering
7
+ # @return [String]
8
+ TABLE_NAME = 'partitions'
9
+ private_constant :TABLE_NAME
10
+
11
+ class << self
12
+ # @param options [Hash]
13
+ # @return [Array<String>]
14
+ def extract_event_types_filter(options)
15
+ options in { filter: { event_types: Array => event_types } }
16
+ event_types&.select! do
17
+ _1.is_a?(String)
18
+ end
19
+ event_types || []
20
+ end
21
+
22
+ # @param options [Hash]
23
+ # @return [Array<Hash[Symbol, String]>]
24
+ def extract_streams_filter(options)
25
+ options in { filter: { streams: Array => streams } }
26
+ streams = streams&.map do
27
+ _1 in { context: String | NilClass => context }
28
+ _1 in { stream_name: String | NilClass => stream_name }
29
+ { context: context, stream_name: stream_name }
30
+ end
31
+ streams || []
32
+ end
33
+ end
34
+
35
+ # @return [String]
36
+ def to_table_name
37
+ TABLE_NAME
38
+ end
39
+
40
+ # @param context [String, nil]
41
+ # @param stream_name [String, nil]
42
+ # @return [void]
43
+ def add_stream_attrs(context: nil, stream_name: nil)
44
+ stream_attrs = { context: context, stream_name: stream_name }
45
+ return unless correct_stream_filter?(stream_attrs)
46
+
47
+ stream_attrs.compact!
48
+ sql = stream_attrs.map do |attr, _|
49
+ "#{to_table_name}.#{attr} = ?"
50
+ end.join(" AND ")
51
+ @sql_builder.where_or(sql, *stream_attrs.values)
52
+ end
53
+
54
+ # @param event_types [Array<String>]
55
+ # @return [void]
56
+ def add_event_types(event_types)
57
+ return if event_types.empty?
58
+
59
+ @sql_builder.where("#{to_table_name}.event_type = ANY(?::varchar[])", event_types)
60
+ end
61
+
62
+ # @return [void]
63
+ def with_event_types
64
+ @sql_builder.where('event_type IS NOT NULL')
65
+ end
66
+
67
+ private
68
+
69
+ # @param stream_attrs [Hash]
70
+ # @return [Boolean]
71
+ def correct_stream_filter?(stream_attrs)
72
+ result = (stream_attrs in { context: String, stream_name: String } | { context: String, stream_name: nil })
73
+ return true if result
74
+
75
+ PgEventstore&.logger&.debug(<<~TEXT)
76
+ Ignoring unsupported stream filter format for grouped read #{stream_attrs.compact.inspect}. \
77
+ See docs/reading_events.md docs for supported formats.
78
+ TEXT
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
@@ -4,6 +4,16 @@ module PgEventstore
4
4
  # Deadly simple SQL builder
5
5
  # @!visibility private
6
6
  class SQLBuilder
7
+ class << self
8
+ # @param builders [Array<PgEventstore::SQLBuilder>]
9
+ # @return [PgEventstore::SQLBuilder]
10
+ def union_builders(builders)
11
+ builders[1..].each_with_object(builders[0]) do |builder, first_builder|
12
+ first_builder.union(builder)
13
+ end
14
+ end
15
+ end
16
+
7
17
  def initialize
8
18
  @select_values = []
9
19
  @from_value = nil
@@ -127,7 +127,7 @@ module PgEventstore
127
127
  def subscriptions_events(query_options)
128
128
  return {} if query_options.empty?
129
129
 
130
- final_builder = union_builders(query_options.map { |id, opts| query_builder(id, opts) })
130
+ final_builder = SQLBuilder.union_builders(query_options.map { |id, opts| query_builder(id, opts) })
131
131
  raw_events = connection.with do |conn|
132
132
  conn.exec_params(*final_builder.to_exec_params)
133
133
  end.to_a
@@ -177,14 +177,6 @@ module PgEventstore
177
177
  builder.select("#{id} as runner_id")
178
178
  end
179
179
 
180
- # @param builders [Array<PgEventstore::SQLBuilder>]
181
- # @return [PgEventstore::SQLBuilder]
182
- def union_builders(builders)
183
- builders[1..].each_with_object(builders[0]) do |builder, first_builder|
184
- first_builder.union(builder)
185
- end
186
- end
187
-
188
180
  # @return [PgEventstore::TransactionQueries]
189
181
  def transaction_queries
190
182
  TransactionQueries.new(connection)
@@ -85,7 +85,8 @@ module PgEventstore
85
85
  def write_to_file(file_path, content)
86
86
  file = File.open(file_path, "w")
87
87
  file.write(content)
88
- file.close
88
+ ensure
89
+ file&.close
89
90
  end
90
91
 
91
92
  # @param file_path [String]
@@ -99,10 +100,10 @@ module PgEventstore
99
100
  # @return [String, nil]
100
101
  def read_pid(file_path)
101
102
  file = File.open(file_path, "r")
102
- file.readline.strip.tap do
103
- file.close
104
- end
103
+ file.readline.strip
105
104
  rescue Errno::ENOENT
105
+ ensure
106
+ file&.close
106
107
  end
107
108
 
108
109
  # @param exception [StandardError]
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PgEventstore
4
4
  # @return [String]
5
- VERSION = "1.11.0"
5
+ VERSION = "1.12.0"
6
6
  end
@@ -27,7 +27,7 @@ module PgEventstore
27
27
  helpers(Paginator::Helpers, Subscriptions::Helpers) do
28
28
  # @return [Array<Hash>, nil]
29
29
  def streams_filter
30
- params in { filter: { streams: Array => streams } }
30
+ streams = QueryBuilders::EventsFiltering.extract_streams_filter(params)
31
31
  streams&.select { _1 in { context: String, stream_name: String, stream_id: String } }&.map do
32
32
  Hash[_1.reject { |_, value| value == '' }].transform_keys(&:to_sym)
33
33
  end&.reject { _1.empty? }
@@ -41,8 +41,9 @@ module PgEventstore
41
41
 
42
42
  # @return [Array<String>, nil]
43
43
  def events_filter
44
- params in { filter: { events: Array => events } }
45
- events&.select { _1.is_a?(String) && _1 != '' }
44
+ event_filters = { filter: { event_types: params.dig(:filter, :events) } }
45
+ events = QueryBuilders::EventsFiltering.extract_event_types_filter(event_filters)
46
+ events&.reject { _1 == '' }
46
47
  end
47
48
 
48
49
  # @return [Symbol]
data/lib/pg_eventstore.rb CHANGED
@@ -9,6 +9,7 @@ require_relative 'pg_eventstore/extensions/callback_handlers_extension'
9
9
  require_relative 'pg_eventstore/extensions/using_connection_extension'
10
10
  require_relative 'pg_eventstore/event_class_resolver'
11
11
  require_relative 'pg_eventstore/config'
12
+ require_relative 'pg_eventstore/partition'
12
13
  require_relative 'pg_eventstore/event'
13
14
  require_relative 'pg_eventstore/stream'
14
15
  require_relative 'pg_eventstore/commands'
@@ -26,6 +26,8 @@ module PgEventstore
26
26
  # _@param_ `middlewares` — provide a list of middleware names to override a config's middlewares
27
27
  def read: (PgEventstore::Stream stream, ?options: ::Hash[untyped, untyped], ?middlewares: ::Array[::Symbol]?) -> ::Array[PgEventstore::Event]
28
28
 
29
+ def read_grouped: (Stream stream, ?options: Hash[untyped, untyped], ?middlewares: ::Array[::Symbol]?) -> ::Array[PgEventstore::Event]
30
+
29
31
  # _@param_ `stream`
30
32
  #
31
33
  # _@param_ `options` — request options
@@ -0,0 +1,16 @@
1
+ module PgEventstore
2
+ module Commands
3
+ class AllStreamReadGrouped < AbstractCommand
4
+
5
+ def call: (Stream stream, ?options: ::Hash[untyped, untyped]) -> Array[Event]
6
+
7
+ private
8
+
9
+ def build_filter_options_for_partitions: (Partition partition, Hash[untyped, untyped] options) -> Hash[untyped, untyped]
10
+
11
+ def build_filter_options_for_streams: (Partition partition, Array[String] stream_ids, Hash[untyped, untyped] options) -> Array[Hash[untyped, untyped]]
12
+
13
+ def group_stream_ids: (Hash[untyped, untyped] options) -> Hash[untyped, untyped]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ module PgEventstore
2
+ module Commands
3
+ class RegularStreamReadGrouped < AbstractCommand
4
+
5
+ def call: (Stream stream, ?options: ::Hash[untyped, untyped]) -> Array[Event]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module PgEventstore
2
+ class Partition
3
+ include Extensions::OptionsExtension
4
+
5
+ attr_accessor id: Integer
6
+
7
+ attr_accessor context: String
8
+
9
+ attr_accessor stream_name: String?
10
+
11
+ attr_accessor event_type: String?
12
+
13
+ attr_accessor table_name: String
14
+ end
15
+ end
@@ -10,6 +10,8 @@ module PgEventstore
10
10
  # _@param_ `event`
11
11
  def event_exists?: (PgEventstore::Event event) -> bool
12
12
 
13
+ def grouped_events: (Stream stream, Array[Hash[untyped, untyped]] options_by_event_type, **untyped options)-> Array[Event]
14
+
13
15
  # _@param_ `events`
14
16
  def ids_from_db: (::Array[PgEventstore::Event] events) -> ::Array[String]
15
17
 
@@ -39,6 +39,8 @@ module PgEventstore
39
39
  # _@return_ — partition attributes
40
40
  def context_partition: (PgEventstore::Stream stream) -> ::Hash[untyped, untyped]?
41
41
 
42
+ def partitions: (Array[Hash[Symbol, String | nil]] stream_filters, Array[String] event_filters)-> Array[Partition]
43
+
42
44
  # _@param_ `stream`
43
45
  #
44
46
  # _@return_ — partition attributes
@@ -70,5 +72,9 @@ module PgEventstore
70
72
 
71
73
  # Returns the value of attribute connection.
72
74
  attr_accessor connection: PgEventstore::Connection
75
+
76
+ private
77
+
78
+ def deserialize: (Hash[untyped, untyped] attrs)-> Partition
73
79
  end
74
80
  end
@@ -0,0 +1,15 @@
1
+ module PgEventstore
2
+ module QueryBuilders
3
+ class BasicFiltering
4
+ @sql_builder: SQLBuilder
5
+
6
+ def initialize: -> void
7
+
8
+ def to_exec_params: -> [String, Array[untyped]]
9
+
10
+ def to_sql_builder: -> SQLBuilder
11
+
12
+ def to_table_name: -> String
13
+ end
14
+ end
15
+ end
@@ -1,39 +1,37 @@
1
1
  module PgEventstore
2
2
  module QueryBuilders
3
3
  # @!visibility private
4
- class EventsFiltering
4
+ class EventsFiltering < BasicFiltering
5
5
  DEFAULT_LIMIT: Integer
6
6
  SQL_DIRECTIONS: Hash[String | Symbol, String]
7
7
  SUBSCRIPTIONS_OPTIONS: ::Array[Symbol]
8
8
 
9
- @sql_builder: PgEventstore::SQLBuilder
9
+ TABLE_NAME: String
10
+
11
+ def self.events_filtering: (Stream stream, ::Hash[untyped, untyped] options) -> EventsFiltering
12
+
13
+ def self.extract_event_types_filter: (Hash[untyped, untyped] options) -> Array[String]
14
+
15
+ def self.extract_streams_filter: (Hash[untyped, untyped] options) -> Array[Hash[untyped, untyped]]
10
16
 
11
- def self.events_filtering: (PgEventstore::Stream stream, ::Hash[untyped, untyped] options) -> PgEventstore::QueryBuilders::EventsFiltering
12
-
13
17
  # _@param_ `options`
14
- def self.subscriptions_events_filtering: (::Hash[untyped, untyped] options) -> PgEventstore::QueryBuilders::EventsFiltering
18
+ def self.subscriptions_events_filtering: (::Hash[untyped, untyped] options) -> EventsFiltering
15
19
 
16
20
  # _@param_ `options`
17
- def self.all_stream_filtering: (::Hash[untyped, untyped] options) -> PgEventstore::QueryBuilders::EventsFiltering
21
+ def self.all_stream_filtering: (::Hash[untyped, untyped] options) -> EventsFiltering
18
22
 
19
23
  # _@param_ `stream`
20
24
  #
21
25
  # _@param_ `options`
22
- def self.specific_stream_filtering: (PgEventstore::Stream stream, ::Hash[untyped, untyped] options) -> PgEventstore::QueryBuilders::EventsFiltering
26
+ def self.specific_stream_filtering: (Stream stream, ::Hash[untyped, untyped] options) -> EventsFiltering
23
27
 
24
- def self.system_stream_filtering: (PgEventstore::Stream stream, Hash[untyped, untyped] options) -> PgEventstore::QueryBuilders::EventsFiltering
28
+ def self.system_stream_filtering: (Stream stream, Hash[untyped, untyped] options) -> EventsFiltering
25
29
 
26
30
  def initialize: () -> void
27
31
 
28
- # _@param_ `context`
29
- #
30
- # _@param_ `stream_name`
31
- #
32
- # _@param_ `stream_id`
33
32
  def add_stream_attrs: (?context: String?, ?stream_name: String?, ?stream_id: String?) -> void
34
33
 
35
- # _@param_ `event_types`
36
- def add_event_types: (::Array[String]? event_types) -> void
34
+ def add_event_types: (::Array[String] event_types) -> void
37
35
 
38
36
  # _@param_ `revision`
39
37
  #
@@ -54,9 +52,11 @@ module PgEventstore
54
52
  # _@param_ `limit`
55
53
  def add_limit: (Integer? limit) -> void
56
54
 
57
- def to_sql_builder: () -> PgEventstore::SQLBuilder
55
+ def set_source: (String table_name)-> void
56
+
57
+ def to_sql_builder: () -> SQLBuilder
58
58
 
59
- def to_exec_params: () -> ::Array[untyped]
59
+ def to_exec_params: () -> [String, Array[untyped]]
60
60
 
61
61
  # _@param_ `stream_attrs`
62
62
  def correct_stream_filter?: (::Hash[untyped, untyped] stream_attrs) -> bool
@@ -0,0 +1,21 @@
1
+ module PgEventstore
2
+ module QueryBuilders
3
+ class PartitionsFiltering < BasicFiltering
4
+ TABLE_NAME: String
5
+
6
+ def self.extract_event_types_filter: (Hash[untyped, untyped] options) -> Array[String]
7
+
8
+ def self.extract_streams_filter: (Hash[untyped, untyped] options) -> Array[Hash[untyped, untyped]]
9
+
10
+ def add_event_types: (::Array[String] event_types) -> void
11
+
12
+ def add_stream_attrs: (?context: String?, ?stream_name: String?) -> void
13
+
14
+ def with_event_types: -> void
15
+
16
+ private
17
+
18
+ def correct_stream_filter?: (::Hash[untyped, untyped] stream_attrs) -> bool
19
+ end
20
+ end
21
+ end
@@ -46,7 +46,7 @@ module PgEventstore
46
46
 
47
47
  def remove_group: () -> self
48
48
 
49
- def to_exec_params: () -> ::Array[(String | ::Array[untyped])]
49
+ def to_exec_params: () -> [String, ::Array[untyped]]
50
50
 
51
51
  def positional_values: () -> ::Array[Object]
52
52
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_eventstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.0
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Dzyzenko
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-05-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: pg
@@ -116,6 +115,7 @@ files:
116
115
  - lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb
117
116
  - lib/pg_eventstore/client.rb
118
117
  - lib/pg_eventstore/commands.rb
118
+ - lib/pg_eventstore/commands/all_stream_read_grouped.rb
119
119
  - lib/pg_eventstore/commands/append.rb
120
120
  - lib/pg_eventstore/commands/delete_event.rb
121
121
  - lib/pg_eventstore/commands/delete_stream.rb
@@ -124,6 +124,7 @@ files:
124
124
  - lib/pg_eventstore/commands/link_to.rb
125
125
  - lib/pg_eventstore/commands/multiple.rb
126
126
  - lib/pg_eventstore/commands/read.rb
127
+ - lib/pg_eventstore/commands/regular_stream_read_grouped.rb
127
128
  - lib/pg_eventstore/commands/regular_stream_read_paginated.rb
128
129
  - lib/pg_eventstore/commands/system_stream_read_paginated.rb
129
130
  - lib/pg_eventstore/config.rb
@@ -139,6 +140,7 @@ files:
139
140
  - lib/pg_eventstore/extensions/using_connection_extension.rb
140
141
  - lib/pg_eventstore/maintenance.rb
141
142
  - lib/pg_eventstore/middleware.rb
143
+ - lib/pg_eventstore/partition.rb
142
144
  - lib/pg_eventstore/pg_connection.rb
143
145
  - lib/pg_eventstore/queries.rb
144
146
  - lib/pg_eventstore/queries/event_queries.rb
@@ -146,7 +148,9 @@ files:
146
148
  - lib/pg_eventstore/queries/maintenance_queries.rb
147
149
  - lib/pg_eventstore/queries/partition_queries.rb
148
150
  - lib/pg_eventstore/queries/transaction_queries.rb
151
+ - lib/pg_eventstore/query_builders/basic_filtering.rb
149
152
  - lib/pg_eventstore/query_builders/events_filtering.rb
153
+ - lib/pg_eventstore/query_builders/partitions_filtering.rb
150
154
  - lib/pg_eventstore/rspec/has_option_matcher.rb
151
155
  - lib/pg_eventstore/rspec/test_helpers.rb
152
156
  - lib/pg_eventstore/sql_builder.rb
@@ -271,6 +275,7 @@ files:
271
275
  - sig/pg_eventstore/cli/try_unlock_subscriptions_set.rbs
272
276
  - sig/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rbs
273
277
  - sig/pg_eventstore/client.rbs
278
+ - sig/pg_eventstore/commands/all_stream_read_grouped.rbs
274
279
  - sig/pg_eventstore/commands/append.rbs
275
280
  - sig/pg_eventstore/commands/delete_event.rbs
276
281
  - sig/pg_eventstore/commands/delete_stream.rbs
@@ -279,6 +284,7 @@ files:
279
284
  - sig/pg_eventstore/commands/link_to.rbs
280
285
  - sig/pg_eventstore/commands/multiple.rbs
281
286
  - sig/pg_eventstore/commands/read.rbs
287
+ - sig/pg_eventstore/commands/regular_stream_read_grouped.rbs
282
288
  - sig/pg_eventstore/commands/regular_stream_read_paginated.rbs
283
289
  - sig/pg_eventstore/commands/system_stream_read_paginated.rbs
284
290
  - sig/pg_eventstore/config.rbs
@@ -294,6 +300,7 @@ files:
294
300
  - sig/pg_eventstore/extensions/using_connection_extension.rbs
295
301
  - sig/pg_eventstore/maintenance.rbs
296
302
  - sig/pg_eventstore/middleware.rbs
303
+ - sig/pg_eventstore/partition.rbs
297
304
  - sig/pg_eventstore/pg_connection.rbs
298
305
  - sig/pg_eventstore/queries.rbs
299
306
  - sig/pg_eventstore/queries/event_queries.rbs
@@ -301,7 +308,9 @@ files:
301
308
  - sig/pg_eventstore/queries/maintenance_queries.rbs
302
309
  - sig/pg_eventstore/queries/partition_queries.rbs
303
310
  - sig/pg_eventstore/queries/transaction_queries.rbs
311
+ - sig/pg_eventstore/query_builders/basic_filtering.rbs
304
312
  - sig/pg_eventstore/query_builders/events_filtering_query.rbs
313
+ - sig/pg_eventstore/query_builders/partitions_filtering.rbs
305
314
  - sig/pg_eventstore/sql_builder.rbs
306
315
  - sig/pg_eventstore/stream.rbs
307
316
  - sig/pg_eventstore/subscriptions/basic_runner.rbs
@@ -368,7 +377,6 @@ metadata:
368
377
  homepage_uri: https://github.com/yousty/pg_eventstore
369
378
  source_code_uri: https://github.com/yousty/pg_eventstore
370
379
  changelog_uri: https://github.com/yousty/pg_eventstore/blob/main/CHANGELOG.md
371
- post_install_message:
372
380
  rdoc_options: []
373
381
  require_paths:
374
382
  - lib
@@ -383,8 +391,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
383
391
  - !ruby/object:Gem::Version
384
392
  version: '0'
385
393
  requirements: []
386
- rubygems_version: 3.5.22
387
- signing_key:
394
+ rubygems_version: 3.6.7
388
395
  specification_version: 4
389
396
  summary: EventStore implementation using PostgreSQL
390
397
  test_files: []