pg_eventstore 1.10.0 → 1.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3ff0f3d0cf7c80ebe381ca1da32b921766c10055832f05b94d15e2f25b73d55
4
- data.tar.gz: a00e2ef1bc9435b2e5b7732e0dd5ee85a3a3ed6e457c5d106f654f035ff7c3e3
3
+ metadata.gz: 402dc90a012b5e1ee934da4f62c4af4ba62e69693a45dac1a84d461950da5717
4
+ data.tar.gz: 6c5d1fdc2e9475eca72b5ef27d0081e0aec6331f76639b7172e676d8b6e63e3d
5
5
  SHA512:
6
- metadata.gz: 23c6bd66a79199cf49411e73635d4829c5bef802a97c3e37f3d46513a1c7b8163163fa665b99d47bd870829ba2059650c4d8b9798c95991c37834e478b352aa4
7
- data.tar.gz: af188405be2d0a13c8694840dc6dcb5eef435ada27979a2f570be1ccacf33c465930d4bb9237ad6d7c78c7bde2edc5d0cfa61064dd7dc8c2c0c6a11856f39980
6
+ metadata.gz: b20c1124f2108b671bd28f3677fb3b2ae88f3b19d2470eb5a31102eb597d4bd5af1080f9bd337b1528107eef2e4bce6b882d434aa8536abc0a5a80b373bfe3a3
7
+ data.tar.gz: 9fd5af079cedc277e278b2e0b50e628601880a81ec5bf7c94700595f6472be389b2fa3d3a2ba2f5d311c8844da40de2c4a52e4dc6c2bda2ca3559e94b267f7f2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.11.0]
4
+
5
+ - 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.
6
+ - Improve long payloads in JSON preview in admin web UI in the way it does not moves content out of the visible area.
7
+ - Admin UI: adjust events filtering and displaying of stream context, stream name, stream id and event type when values of them contain empty strings or non-displayable characters
8
+
3
9
  ## [1.10.0]
4
10
  - Admin UI: Adjust `SubscriptionSet` "Stop"/"Delete" buttons appearance. Now if `SubscriptionsSet` is not alive anymore(the related process is dead or does not exist anymore) - "Delete" button is shown. If `SubscriptionSet` is alive - "Stop" button is shown
5
11
  - Admin IU: fixed several potential XSS vulnerabilities
@@ -243,4 +243,14 @@ module PgEventstore
243
243
  "Too many records of #{stream.to_hash.inspect} stream to lock: #{number_of_records}"
244
244
  end
245
245
  end
246
+
247
+ class WrappedException < Error
248
+ attr_reader :original_exception
249
+ attr_reader :extra
250
+
251
+ def initialize(original_exception, extra)
252
+ @original_exception = original_exception
253
+ @extra = extra
254
+ end
255
+ end
246
256
  end
@@ -16,9 +16,9 @@ module PgEventstore
16
16
  callbacks.run_callbacks(:process, Utils.original_global_position(raw_event)) do
17
17
  handler.call(raw_event)
18
18
  end
19
- rescue
19
+ rescue => exception
20
20
  raw_events.unshift(raw_event)
21
- raise
21
+ raise Utils.wrap_exception(exception, global_position: Utils.original_global_position(raw_event))
22
22
  end
23
23
 
24
24
  # @param callbacks [PgEventstore::Callbacks]
@@ -26,7 +26,7 @@ module PgEventstore
26
26
  end
27
27
 
28
28
  # @param subscription [PgEventstore::Subscription]
29
- # @param error [StandardError]
29
+ # @param error [PgEventstore::WrappedException]
30
30
  # @return [void]
31
31
  def update_subscription_error(subscription, error)
32
32
  subscription.update(last_error: Utils.error_info(error), last_error_occurred_at: Time.now.utc)
@@ -36,13 +36,13 @@ module PgEventstore
36
36
  # @param restart_terminator [#call, nil]
37
37
  # @param failed_subscription_notifier [#call, nil]
38
38
  # @param events_processor [PgEventstore::EventsProcessor]
39
- # @param error [StandardError]
39
+ # @param error [PgEventstore::WrappedException]
40
40
  # @return [void]
41
41
  def restart_events_processor(subscription, restart_terminator, failed_subscription_notifier, events_processor,
42
42
  error)
43
43
  return if restart_terminator&.call(subscription.dup)
44
44
  if subscription.restart_count >= subscription.max_restarts_number
45
- return failed_subscription_notifier&.call(subscription.dup, error)
45
+ return failed_subscription_notifier&.call(subscription.dup, Utils.unwrap_exception(error))
46
46
  end
47
47
 
48
48
  Thread.new do
@@ -46,11 +46,14 @@ module PgEventstore
46
46
  # @param error [StandardError]
47
47
  # @return [Hash]
48
48
  def error_info(error)
49
+ original_error = unwrap_exception(error)
49
50
  {
50
- class: error.class,
51
- message: error.message,
52
- backtrace: error.backtrace
53
- }
51
+ class: original_error.class,
52
+ message: original_error.message,
53
+ backtrace: original_error.backtrace
54
+ }.tap do |attrs|
55
+ attrs.merge!(error.extra) if error.is_a?(WrappedException)
56
+ end
54
57
  end
55
58
 
56
59
  # @param str [String]
@@ -101,6 +104,21 @@ module PgEventstore
101
104
  end
102
105
  rescue Errno::ENOENT
103
106
  end
107
+
108
+ # @param exception [StandardError]
109
+ # @param extra [Hash] additional exception info
110
+ # @return [PgEventstore::WrappedException]
111
+ def wrap_exception(exception, **extra)
112
+ WrappedException.new(exception, extra)
113
+ end
114
+
115
+ # @param wrapped_exception [StandardError, PgEventstore::WrappedException]
116
+ # @return [StandardError]
117
+ def unwrap_exception(wrapped_exception)
118
+ return wrapped_exception.original_exception if wrapped_exception.is_a?(WrappedException)
119
+
120
+ wrapped_exception
121
+ end
104
122
  end
105
123
  end
106
124
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PgEventstore
4
4
  # @return [String]
5
- VERSION = "1.10.0"
5
+ VERSION = "1.11.0"
6
6
  end
@@ -13,6 +13,11 @@ module PgEventstore
13
13
  # @return [String]
14
14
  COOKIES_FLASH_MESSAGE_KEY = 'flash_message'
15
15
 
16
+ # Defines a replacement for empty string value in a stream attributes filter or in an event type filter. This
17
+ # replacement is needed to differentiate a user selection vs default placeholder value.
18
+ # @return [String]
19
+ EMPTY_STRING_SIGN = "\x00".freeze
20
+
16
21
  set :static_cache_control, [:private, max_age: 86400]
17
22
  set :environment, -> { (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['APP_ENV'])&.to_sym || :development }
18
23
  set :logging, -> { environment == :development || environment == :test }
@@ -69,8 +74,11 @@ module PgEventstore
69
74
  # @param collection [PgEventstore::Web::Paginator::BaseCollection]
70
75
  # @return [void]
71
76
  def paginated_json_response(collection)
77
+ results = collection.collection.map do |attrs|
78
+ attrs.transform_values { escape_empty_string(_1) }
79
+ end
72
80
  halt 200, {
73
- results: collection.collection,
81
+ results: results,
74
82
  pagination: { more: !collection.next_page_starting_id.nil?, starting_id: collection.next_page_starting_id }
75
83
  }.to_json
76
84
  end
@@ -108,9 +116,25 @@ module PgEventstore
108
116
  COOKIES_FLASH_MESSAGE_KEY, { value: val, http_only: false, same_site: :lax, path: '/' }
109
117
  )
110
118
  end
119
+
120
+ # @param string [String, nil]
121
+ # @return [String, nil]
122
+ def escape_empty_string(string)
123
+ string == '' ? EMPTY_STRING_SIGN : string
124
+ end
125
+
126
+ # @param string [String, nil]
127
+ # @return [String, nil]
128
+ def unescape_empty_string(string)
129
+ string == EMPTY_STRING_SIGN ? '' : string
130
+ end
111
131
  end
112
132
 
113
133
  get '/' do
134
+ streams_filter = self.streams_filter&.map do |attrs|
135
+ attrs.transform_values { unescape_empty_string(_1) }
136
+ end
137
+ events_filter = self.events_filter&.map(&method(:unescape_empty_string))
114
138
  @collection = Paginator::EventsCollection.new(
115
139
  current_config,
116
140
  starting_id: params[:starting_id]&.to_i,
@@ -169,7 +193,7 @@ module PgEventstore
169
193
  get '/stream_contexts_filtering', provides: :json do
170
194
  collection = Paginator::StreamContextsCollection.new(
171
195
  current_config,
172
- starting_id: params[:starting_id],
196
+ starting_id: unescape_empty_string(params[:starting_id]),
173
197
  per_page: Paginator::StreamContextsCollection::PER_PAGE,
174
198
  order: :asc,
175
199
  options: { query: params[:term] }
@@ -180,10 +204,10 @@ module PgEventstore
180
204
  get '/stream_names_filtering', provides: :json do
181
205
  collection = Paginator::StreamNamesCollection.new(
182
206
  current_config,
183
- starting_id: params[:starting_id],
207
+ starting_id: unescape_empty_string(params[:starting_id]),
184
208
  per_page: Paginator::StreamNamesCollection::PER_PAGE,
185
209
  order: :asc,
186
- options: { query: params[:term], context: params[:context] }
210
+ options: { query: params[:term], context: unescape_empty_string(params[:context]) }
187
211
  )
188
212
  paginated_json_response(collection)
189
213
  end
@@ -191,10 +215,14 @@ module PgEventstore
191
215
  get '/stream_ids_filtering', provides: :json do
192
216
  collection = Paginator::StreamIdsCollection.new(
193
217
  current_config,
194
- starting_id: params[:starting_id],
218
+ starting_id: unescape_empty_string(params[:starting_id]),
195
219
  per_page: Paginator::StreamIdsCollection::PER_PAGE,
196
220
  order: :asc,
197
- options: { query: params[:term], context: params[:context], stream_name: params[:stream_name] }
221
+ options: {
222
+ query: params[:term],
223
+ context: unescape_empty_string(params[:context]),
224
+ stream_name: unescape_empty_string(params[:stream_name])
225
+ }
198
226
  )
199
227
  paginated_json_response(collection)
200
228
  end
@@ -202,7 +230,7 @@ module PgEventstore
202
230
  get '/event_types_filtering', provides: :json do
203
231
  collection = Paginator::EventTypesCollection.new(
204
232
  current_config,
205
- starting_id: params[:starting_id],
233
+ starting_id: unescape_empty_string(params[:starting_id]),
206
234
  per_page: Paginator::EventTypesCollection::PER_PAGE,
207
235
  order: :asc,
208
236
  options: { query: params[:term] }
@@ -88,9 +88,9 @@ module PgEventstore
88
88
  filter: {
89
89
  streams: [
90
90
  {
91
- context: event.stream.context,
92
- stream_name: event.stream.stream_name,
93
- stream_id: event.stream.stream_id
91
+ context: escape_empty_string(event.stream.context),
92
+ stream_name: escape_empty_string(event.stream.stream_name),
93
+ stream_id: escape_empty_string(event.stream.stream_id)
94
94
  }
95
95
  ]
96
96
  }
@@ -98,6 +98,14 @@ module PgEventstore
98
98
  )
99
99
  end
100
100
 
101
+ # @param str [String]
102
+ # @return [String]
103
+ def empty_characters_fallback(str)
104
+ return str unless str.strip == ''
105
+
106
+ '<i>Non-printable characters</i>'
107
+ end
108
+
101
109
  private
102
110
 
103
111
  # @param starting_id [String, Integer, nil]
@@ -1,6 +1,30 @@
1
1
  $(function(){
2
2
  "use strict";
3
3
 
4
+ const EMPTY_STRING_SIGN = "\x00";
5
+
6
+ let templateSelection = function(state){
7
+ if (!state.id)
8
+ return state.text;
9
+
10
+ return filterValueToHuman(state.element && state.element.value, state.text);
11
+ }
12
+
13
+ let templateResult = function(item){
14
+ return filterValueToHuman(item.text);
15
+ }
16
+
17
+ let filterValueToHuman = function(str, alternativeStr){
18
+ if(str === EMPTY_STRING_SIGN)
19
+ return $("<i>Empty String</i>");
20
+
21
+ let result = alternativeStr || str;
22
+ if($.trim(result.replaceAll(EMPTY_STRING_SIGN, '')) === '')
23
+ return $("<i>Non-printable characters</i>");
24
+
25
+ return result;
26
+ }
27
+
4
28
  let initStreamFilterAutocomplete = function($filter) {
5
29
  let $contextSelect = $filter.find('select[name*="context"]');
6
30
  let $streamNameSelect = $filter.find('select[name*="stream_name"]');
@@ -22,7 +46,9 @@ $(function(){
22
46
  return data;
23
47
  },
24
48
  },
25
- allowClear: true
49
+ allowClear: true,
50
+ templateSelection: templateSelection,
51
+ templateResult: templateResult,
26
52
  });
27
53
  $streamNameSelect.select2({
28
54
  ajax: {
@@ -41,7 +67,9 @@ $(function(){
41
67
  return data;
42
68
  },
43
69
  },
44
- allowClear: true
70
+ allowClear: true,
71
+ templateSelection: templateSelection,
72
+ templateResult: templateResult,
45
73
  });
46
74
  $streamIdSelect.select2({
47
75
  ajax: {
@@ -61,7 +89,9 @@ $(function(){
61
89
  return data;
62
90
  },
63
91
  },
64
- allowClear: true
92
+ allowClear: true,
93
+ templateSelection: templateSelection,
94
+ templateResult: templateResult,
65
95
  });
66
96
  $contextSelect.on('change.select2', removeDeleteBtn);
67
97
  $streamNameSelect.on('change.select2', removeDeleteBtn);
@@ -89,7 +119,9 @@ $(function(){
89
119
  return data;
90
120
  },
91
121
  },
92
- allowClear: true
122
+ allowClear: true,
123
+ templateSelection: templateSelection,
124
+ templateResult: templateResult,
93
125
  });
94
126
  }
95
127
 
@@ -130,6 +162,11 @@ $(function(){
130
162
  initEventTypeFilterAutocomplete($filtersForm.find('.event-filters').children().last());
131
163
  });
132
164
 
165
+ // Because zero-byte character can't be rendered into HTML properly - loop through each option, marked as containing
166
+ // zero-byte value and assign zero-byte value explicitly. This must be done before we initialize select2 plugin.
167
+ $('option.zero-byte-val').each(function(){
168
+ this.value = EMPTY_STRING_SIGN;
169
+ });
133
170
  // Init select2 for stream filters which were initially rendered
134
171
  $filtersForm.find('.stream-filters').children().each(function(){
135
172
  initStreamFilterAutocomplete($(this));
@@ -4,6 +4,18 @@ tr.collapsing {
4
4
  display: none;
5
5
  }
6
6
 
7
+ td.json-cell {
8
+ max-width: 1px;
9
+ }
10
+
11
+ td.json-cell pre {
12
+ display: block;
13
+ width: 100%;
14
+ box-sizing: border-box;
15
+ overflow-x: auto;
16
+ white-space: pre;
17
+ }
18
+
7
19
  #confirmation-modal .modal-body {
8
20
  font-size: 1.2rem;
9
21
  }
@@ -3,7 +3,11 @@
3
3
  <select name="filter[events][]" class="form-control mb-2" data-placeholder="Event type" data-url="<%= url('/event_types_filtering') %>" autocomplete="off">
4
4
  <option></option>
5
5
  <% if event_type %>
6
- <option value="<%= h event_type %>" selected><%= h event_type %></option>
6
+ <% if event_type == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
7
+ <option class="zero-byte-val" selected></option>
8
+ <% else %>
9
+ <option value="<%= h event_type %>" selected><%= h event_type %></option>
10
+ <% end %>
7
11
  <% end %>
8
12
  </select>
9
13
  </div>
@@ -2,17 +2,17 @@
2
2
  <tr>
3
3
  <td><%= event.global_position %></td>
4
4
  <td><%= event.stream_revision %></td>
5
- <td><%= h event.stream.context %></td>
6
- <td><%= h event.stream.stream_name %></td>
5
+ <td><%= empty_characters_fallback(h event.stream.context) %></td>
6
+ <td><%= empty_characters_fallback(h event.stream.stream_name) %></td>
7
7
  <td>
8
- <a href="<%= stream_path(event) %>"><%= h event.stream.stream_id %></a>
8
+ <a href="<%= stream_path(event) %>"><%= empty_characters_fallback(h event.stream.stream_id) %></a>
9
9
  <a role="button" href="#" data-title="Copy stream definition." class="copy-to-clipboard"
10
10
  data-clipboard-content="<%= h "PgEventstore::Stream.new(context: #{event.stream.context.inspect}, stream_name: #{event.stream.stream_name.inspect}, stream_id: #{event.stream.stream_id.inspect})" %>">
11
11
  <i class="fa fa-clipboard"></i>
12
12
  </a>
13
13
  </td>
14
14
  <td>
15
- <p class="float-left"><%= h event.type %></p>
15
+ <p class="float-left"><%= empty_characters_fallback(h event.type) %></p>
16
16
  <% if event.link %>
17
17
  <p class="float-left ml-2">
18
18
  <i class="fa fa-link"></i>
@@ -32,7 +32,7 @@
32
32
  </td>
33
33
  </tr>
34
34
  <tr class="event-payload d-none">
35
- <td colspan="8">
35
+ <td colspan="9" class="json-cell">
36
36
  <strong>Data:</strong>
37
37
  <pre><%= h JSON.pretty_generate(event.data) %></pre>
38
38
  <strong>Metadata:</strong>
@@ -3,7 +3,11 @@
3
3
  <select name="filter[streams][][context]" class="form-control mb-2" data-placeholder="Context" data-url="<%= url('/stream_contexts_filtering') %>" autocomplete="off">
4
4
  <option></option>
5
5
  <% if stream[:context] %>
6
- <option value="<%= h stream[:context] %>" selected><%= h stream[:context] %></option>
6
+ <% if stream[:context] == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
7
+ <option class="zero-byte-val" selected></option>
8
+ <% else %>
9
+ <option value="<%= h stream[:context] %>" selected><%= h stream[:context] %></option>
10
+ <% end %>
7
11
  <% end %>
8
12
  </select>
9
13
  </div>
@@ -11,7 +15,11 @@
11
15
  <select name="filter[streams][][stream_name]" class="form-control mb-2" data-placeholder="Stream name" data-url="<%= url('/stream_names_filtering') %>" autocomplete="off">
12
16
  <option></option>
13
17
  <% if stream[:stream_name] %>
14
- <option value="<%= h stream[:stream_name] %>" selected><%= h stream[:stream_name] %></option>
18
+ <% if stream[:stream_name] == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
19
+ <option class="zero-byte-val" selected></option>
20
+ <% else %>
21
+ <option value="<%= h stream[:stream_name] %>" selected><%= h stream[:stream_name] %></option>
22
+ <% end %>
15
23
  <% end %>
16
24
  </select>
17
25
  </div>
@@ -19,7 +27,11 @@
19
27
  <select name="filter[streams][][stream_id]" class="form-control mb-2" data-placeholder="Stream ID" data-url="<%= url('/stream_ids_filtering') %>" autocomplete="off">
20
28
  <option></option>
21
29
  <% if stream[:stream_id] %>
22
- <option value="<%= h stream[:stream_id] %>" selected><%= h stream[:stream_id] %></option>
30
+ <% if stream[:stream_id] == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
31
+ <option class="zero-byte-val" selected></option>
32
+ <% else %>
33
+ <option value="<%= h stream[:stream_id] %>" selected><%= h stream[:stream_id] %></option>
34
+ <% end %>
23
35
  <% end %>
24
36
  </select>
25
37
  </div>
@@ -210,13 +210,13 @@
210
210
  </td>
211
211
  </tr>
212
212
  <tr class="collapse" id="options-<%= subscription.id %>">
213
- <td colspan="16">
213
+ <td colspan="16" class="json-cell">
214
214
  <pre><%= h JSON.pretty_generate(subscription.options) %></pre>
215
215
  </td>
216
216
  </tr>
217
217
  <% if subscription.last_error %>
218
218
  <tr class="collapse" id="last-error-<%= subscription.id %>">
219
- <td colspan="16">
219
+ <td colspan="16" class="json-cell">
220
220
  <pre><%= h JSON.pretty_generate(subscription.last_error) %></pre>
221
221
  </td>
222
222
  </tr>
@@ -123,4 +123,12 @@ module PgEventstore
123
123
 
124
124
  def user_friendly_message: -> String
125
125
  end
126
+
127
+ class WrappedException < PgEventstore::Error
128
+ def initialize: (StandardError original_exception, Hash[Symbol, untyped] extra) -> void
129
+
130
+ attr_accessor original_exception: StandardError
131
+
132
+ attr_accessor extra: Hash[Symbol, untyped]
133
+ end
126
134
  end
@@ -4,11 +4,11 @@ module PgEventstore
4
4
 
5
5
  def self.update_subscription_stats: (PgEventstore::Subscription subscription, PgEventstore::SubscriptionHandlerPerformance stats, Integer current_position) -> void
6
6
 
7
- def self.update_subscription_error: (PgEventstore::Subscription subscription, StandardError error) -> void
7
+ def self.update_subscription_error: (PgEventstore::Subscription subscription, PgEventstore::WrappedException error) -> void
8
8
 
9
9
  def self.restart_events_processor: (PgEventstore::Subscription subscription, _RestartTerminator? restart_terminator,
10
10
  _FailedSubscriptionNotifier? failed_subscription_notifier, PgEventstore::EventsProcessor events_processor,
11
- StandardError error) -> void
11
+ PgEventstore::WrappedException error) -> void
12
12
 
13
13
  def self.update_subscription_chunk_stats: (PgEventstore::Subscription subscription, Integer global_position) -> void
14
14
 
@@ -20,5 +20,9 @@ module PgEventstore
20
20
  def self.underscore_str: (String str) -> String
21
21
 
22
22
  def self.original_global_position: (Hash[untyped, untyped] raw_event) -> Integer
23
+
24
+ def self.unwrap_exception: (PgEventstore::WrappedException | StandardError wrapped_exception)-> StandardError
25
+
26
+ def self.wrap_exception: (StandardError exception, **untyped extra)-> PgEventstore::WrappedException
23
27
  end
24
28
  end
@@ -5,6 +5,8 @@ module PgEventstore
5
5
  COOKIES_FLASH_MESSAGE_KEY: String
6
6
  DEFAULT_ADMIN_UI_CONFIG: Symbol
7
7
 
8
+ EMPTY_STRING_SIGN: String
9
+
8
10
  def asset_url: (String path) -> String
9
11
 
10
12
  def connection: -> PgEventstore::Connection
@@ -13,6 +15,8 @@ module PgEventstore
13
15
 
14
16
  def current_config=: (untyped val) -> void
15
17
 
18
+ def escape_empty_string: (String? string) -> String?
19
+
16
20
  def events_filter: -> Array[String]?
17
21
 
18
22
  def flash_message=: (({ message: String, kind: String }) val)-> String
@@ -30,6 +34,8 @@ module PgEventstore
30
34
  def streams_filter: -> Array[Hash[untyped, untyped]]?
31
35
 
32
36
  def system_stream: -> String?
37
+
38
+ def unescape_empty_string: (String? string) -> String?
33
39
  end
34
40
  end
35
41
  end
@@ -2,6 +2,8 @@ module PgEventstore
2
2
  module Web
3
3
  module Paginator
4
4
  module Helpers
5
+ def empty_characters_fallback: (String str)-> String
6
+
5
7
  # _@param_ `collection`
6
8
  def previous_page_link: (PgEventstore::Web::Paginator::BaseCollection collection) -> String
7
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_eventstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Dzyzenko
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-14 00:00:00.000000000 Z
11
+ date: 2025-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -368,7 +368,7 @@ metadata:
368
368
  homepage_uri: https://github.com/yousty/pg_eventstore
369
369
  source_code_uri: https://github.com/yousty/pg_eventstore
370
370
  changelog_uri: https://github.com/yousty/pg_eventstore/blob/main/CHANGELOG.md
371
- post_install_message:
371
+ post_install_message:
372
372
  rdoc_options: []
373
373
  require_paths:
374
374
  - lib
@@ -383,8 +383,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
383
383
  - !ruby/object:Gem::Version
384
384
  version: '0'
385
385
  requirements: []
386
- rubygems_version: 3.5.3
387
- signing_key:
386
+ rubygems_version: 3.5.22
387
+ signing_key:
388
388
  specification_version: 4
389
389
  summary: EventStore implementation using PostgreSQL
390
390
  test_files: []