pg_eventstore 1.13.1 → 1.13.3

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +126 -0
  3. data/CHANGELOG.md +8 -0
  4. data/exe/pg-eventstore +6 -6
  5. data/lib/pg_eventstore/abstract_command.rb +1 -1
  6. data/lib/pg_eventstore/callbacks.rb +7 -2
  7. data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +1 -0
  8. data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +5 -4
  9. data/lib/pg_eventstore/cli/parser_options/metadata.rb +4 -4
  10. data/lib/pg_eventstore/cli/parsers/default_parser.rb +8 -8
  11. data/lib/pg_eventstore/cli/parsers/subscription_parser.rb +2 -2
  12. data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +4 -1
  13. data/lib/pg_eventstore/cli.rb +4 -4
  14. data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +2 -2
  15. data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +1 -0
  16. data/lib/pg_eventstore/commands/link_to.rb +4 -5
  17. data/lib/pg_eventstore/commands/multiple.rb +1 -3
  18. data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +1 -1
  19. data/lib/pg_eventstore/config.rb +1 -1
  20. data/lib/pg_eventstore/connection.rb +4 -1
  21. data/lib/pg_eventstore/errors.rb +3 -1
  22. data/lib/pg_eventstore/extensions/callbacks_extension.rb +2 -0
  23. data/lib/pg_eventstore/extensions/options_extension.rb +10 -10
  24. data/lib/pg_eventstore/extensions/using_connection_extension.rb +1 -1
  25. data/lib/pg_eventstore/pg_connection.rb +7 -5
  26. data/lib/pg_eventstore/queries/event_queries.rb +11 -10
  27. data/lib/pg_eventstore/queries/partition_queries.rb +4 -4
  28. data/lib/pg_eventstore/queries/transaction_queries.rb +5 -9
  29. data/lib/pg_eventstore/query_builders/events_filtering.rb +13 -13
  30. data/lib/pg_eventstore/query_builders/partitions_filtering.rb +6 -8
  31. data/lib/pg_eventstore/rspec/has_option_matcher.rb +12 -13
  32. data/lib/pg_eventstore/rspec/test_helpers.rb +4 -3
  33. data/lib/pg_eventstore/sql_builder.rb +9 -13
  34. data/lib/pg_eventstore/stream.rb +8 -8
  35. data/lib/pg_eventstore/subscriptions/basic_runner.rb +4 -3
  36. data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +8 -3
  37. data/lib/pg_eventstore/subscriptions/commands_handler.rb +1 -1
  38. data/lib/pg_eventstore/subscriptions/events_processor.rb +8 -4
  39. data/lib/pg_eventstore/subscriptions/extensions/base_command_extension.rb +10 -8
  40. data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +23 -21
  41. data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +11 -8
  42. data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +8 -13
  43. data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_queries.rb +7 -6
  44. data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_connection.rb +3 -1
  45. data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rb +1 -0
  46. data/lib/pg_eventstore/subscriptions/runner_state.rb +3 -1
  47. data/lib/pg_eventstore/subscriptions/subscription.rb +11 -10
  48. data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +1 -1
  49. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +1 -1
  50. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +6 -7
  51. data/lib/pg_eventstore/subscriptions/subscriptions_set.rb +8 -8
  52. data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +2 -0
  53. data/lib/pg_eventstore/subscriptions/synchronized_array.rb +41 -0
  54. data/lib/pg_eventstore/tasks/setup.rake +7 -6
  55. data/lib/pg_eventstore/utils.rb +8 -4
  56. data/lib/pg_eventstore/version.rb +1 -1
  57. data/lib/pg_eventstore/web/application.rb +22 -15
  58. data/lib/pg_eventstore/web/paginator/event_types_collection.rb +9 -11
  59. data/lib/pg_eventstore/web/paginator/events_collection.rb +6 -7
  60. data/lib/pg_eventstore/web/paginator/helpers.rb +19 -19
  61. data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +9 -9
  62. data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +9 -10
  63. data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +9 -11
  64. data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
  65. data/lib/pg_eventstore/web/subscriptions/subscriptions_to_set_association.rb +2 -0
  66. data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
  67. data/lib/pg_eventstore.rb +3 -11
  68. data/pg_eventstore.gemspec +19 -19
  69. data/rbs_collection.lock.yaml +9 -1
  70. data/rbs_collection.yaml +2 -0
  71. data/sig/manifest.yaml +3 -0
  72. data/sig/pg_eventstore/client.rbs +4 -8
  73. data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +2 -1
  74. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +2 -0
  75. data/sig/pg_eventstore/subscriptions/synchronized_array.rbs +4 -0
  76. data/sig/pg_eventstore/web/application.rbs +2 -0
  77. metadata +11 -7
@@ -46,9 +46,10 @@ module PgEventstore
46
46
  def set_names
47
47
  builder = SQLBuilder.new.select('name').from('subscriptions_set').group('name').order('name ASC')
48
48
 
49
- connection.with do |conn|
49
+ raw_set = connection.with do |conn|
50
50
  conn.exec_params(*builder.to_exec_params)
51
- end.map { |attrs| attrs['name'] }
51
+ end
52
+ raw_set.map { |attrs| attrs['name'] }
52
53
  end
53
54
 
54
55
  # The same as #find_all, but returns first result
@@ -61,15 +62,15 @@ module PgEventstore
61
62
  # @return [Hash]
62
63
  # @raise [PgEventstore::RecordNotFound]
63
64
  def find!(id)
64
- find_by(id: id) || raise(RecordNotFound.new("subscriptions_set", id))
65
+ find_by(id: id) || raise(RecordNotFound.new('subscriptions_set', id))
65
66
  end
66
67
 
67
68
  # @param attrs [Hash]
68
69
  # @return [Hash]
69
70
  def create(attrs)
70
71
  sql = <<~SQL
71
- INSERT INTO subscriptions_set (#{attrs.keys.join(', ')})
72
- VALUES (#{Utils.positional_vars(attrs.values)})
72
+ INSERT INTO subscriptions_set (#{attrs.keys.join(', ')})
73
+ VALUES (#{Utils.positional_vars(attrs.values)})
73
74
  RETURNING *
74
75
  SQL
75
76
  pg_result = connection.with do |conn|
@@ -92,7 +93,7 @@ module PgEventstore
92
93
  pg_result = connection.with do |conn|
93
94
  conn.exec_params(sql, [*attrs.values, id])
94
95
  end
95
- raise(RecordNotFound.new("subscriptions_set", id)) if pg_result.ntuples.zero?
96
+ raise(RecordNotFound.new('subscriptions_set', id)) if pg_result.ntuples == 0
96
97
 
97
98
  deserialize(pg_result.to_a.first)
98
99
  end
@@ -17,9 +17,10 @@ module PgEventstore
17
17
  end
18
18
 
19
19
  def recovers?(error)
20
- EXCEPTIONS_TO_HANDLE.any? {error.is_a?(_1) }
20
+ EXCEPTIONS_TO_HANDLE.any? { error.is_a?(_1) }
21
21
  end
22
22
 
23
+ # rubocop:disable Lint/SuppressedException
23
24
  def recover(_error)
24
25
  loop do
25
26
  sleep TIME_BETWEEN_RETRIES
@@ -32,6 +33,7 @@ module PgEventstore
32
33
  rescue *EXCEPTIONS_TO_HANDLE
33
34
  end
34
35
  end
36
+ # rubocop:enable Lint/SuppressedException
35
37
 
36
38
  private
37
39
 
@@ -21,6 +21,7 @@ module PgEventstore
21
21
 
22
22
  def recover(error)
23
23
  return false if @restart_terminator&.call(@subscription.dup)
24
+
24
25
  if @subscription.restart_count >= @subscription.max_restarts_number
25
26
  @failed_subscription_notifier&.call(@subscription.dup, Utils.unwrap_exception(error))
26
27
  return false
@@ -7,7 +7,7 @@ module PgEventstore
7
7
  include Extensions::CallbacksExtension
8
8
 
9
9
  # @return [Hash<Symbol => String>]
10
- STATES = %i(initial running halting stopped dead).to_h { [_1, _1.to_s.freeze] }.freeze
10
+ STATES = %i[initial running halting stopped dead].to_h { [_1, _1.to_s.freeze] }.freeze
11
11
 
12
12
  def initialize
13
13
  initial!
@@ -36,11 +36,13 @@ module PgEventstore
36
36
 
37
37
  # @param state [String]
38
38
  # @return [String]
39
+ # rubocop:disable Naming/AccessorMethodName
39
40
  def set_state(state)
40
41
  old_state = @state
41
42
  @state = state
42
43
  callbacks.run_callbacks(:change_state, @state) unless old_state == @state
43
44
  @state
44
45
  end
46
+ # rubocop:enable Naming/AccessorMethodName
45
47
  end
46
48
  end
@@ -26,10 +26,11 @@ module PgEventstore
26
26
  # the list of available options
27
27
  attribute(:options)
28
28
  # @!attribute current_position
29
- # @return [Integer, nil] current Subscription's position. It is updated automatically each time an event is processed
29
+ # @return [Integer, nil] current Subscription's position. It is updated automatically each time an event is
30
+ # processed
30
31
  attribute(:current_position)
31
32
  # @!attribute state
32
- # @return [String, nil] current Subscription's state. It is updated automatically during Subscription's life cycle.
33
+ # @return [String, nil] current Subscription's state. It is updated automatically during Subscription's life cycle
33
34
  # See {RunnerState::STATES} for possible values.
34
35
  attribute(:state)
35
36
  # @!attribute average_event_processing_time
@@ -141,20 +142,20 @@ module PgEventstore
141
142
  id.hash
142
143
  end
143
144
 
144
- # @param another [Object]
145
+ # @param other [Object]
145
146
  # @return [Boolean]
146
- def eql?(another)
147
- return false unless another.is_a?(Subscription)
147
+ def eql?(other)
148
+ return false unless other.is_a?(Subscription)
148
149
 
149
- hash == another.hash
150
+ hash == other.hash
150
151
  end
151
152
 
152
- # @param another [Object]
153
+ # @param other [Object]
153
154
  # @return [Boolean]
154
- def ==(another)
155
- return false unless another.is_a?(Subscription)
155
+ def ==(other)
156
+ return false unless other.is_a?(Subscription)
156
157
 
157
- id == another.id
158
+ id == other.id
158
159
  end
159
160
 
160
161
  private
@@ -34,7 +34,7 @@ module PgEventstore
34
34
  # The average time required to process an event.
35
35
  # @return [Float]
36
36
  def average_event_processing_time
37
- synchronize { @timings.size.zero? ? 0.0 : @timings.sum / @timings.size }
37
+ synchronize { @timings.empty? ? 0.0 : @timings.sum / @timings.size }
38
38
  end
39
39
  end
40
40
  end
@@ -58,7 +58,7 @@ module PgEventstore
58
58
 
59
59
  # @return [Integer]
60
60
  def estimate_events_number
61
- return INITIAL_EVENTS_PER_CHUNK if @stats.average_event_processing_time.zero?
61
+ return INITIAL_EVENTS_PER_CHUNK if @stats.average_event_processing_time == 0
62
62
 
63
63
  events_per_chunk = (@subscription.chunk_query_interval / @stats.average_event_processing_time).round
64
64
  events_to_fetch = [events_per_chunk, MAX_EVENTS_PER_CHUNK].min - @events_processor.events_left_in_chunk
@@ -6,6 +6,7 @@ require_relative 'basic_runner'
6
6
  require_relative 'runner_recovery_strategy'
7
7
  require_relative 'runner_recovery_strategies'
8
8
  require_relative 'subscription'
9
+ require_relative 'synchronized_array'
9
10
  require_relative 'events_processor'
10
11
  require_relative 'subscription_handler_performance'
11
12
  require_relative 'subscription_runner'
@@ -54,7 +55,7 @@ module PgEventstore
54
55
  {
55
56
  name: set_name,
56
57
  max_restarts_number: max_retries || config.subscriptions_set_max_retries,
57
- time_between_restarts: retries_interval || config.subscriptions_set_retries_interval
58
+ time_between_restarts: retries_interval || config.subscriptions_set_retries_interval,
58
59
  }
59
60
  )
60
61
  @subscriptions_lifecycle = SubscriptionsLifecycle.new(
@@ -108,7 +109,7 @@ module PgEventstore
108
109
  graceful_shutdown_timeout: graceful_shutdown_timeout,
109
110
  recovery_strategies: recovery_strategies(subscription, restart_terminator, failed_subscription_notifier)
110
111
  ),
111
- subscription: subscription,
112
+ subscription: subscription
112
113
  )
113
114
 
114
115
  @subscriptions_lifecycle.runners.push(runner)
@@ -149,12 +150,10 @@ module PgEventstore
149
150
  private
150
151
 
151
152
  # @return [Object] the result of the passed block
152
- def run_cli_callbacks
153
+ def run_cli_callbacks(&blk)
153
154
  return yield unless defined?(::PgEventstore::CLI)
154
155
 
155
- PgEventstore::CLI.callbacks.run_callbacks(:start_manager, self) do
156
- yield
157
- end
156
+ PgEventstore::CLI.callbacks.run_callbacks(:start_manager, self, &blk)
158
157
  end
159
158
 
160
159
  # @param middlewares [Array<Symbol>, nil]
@@ -183,7 +182,7 @@ module PgEventstore
183
182
  RunnerRecoveryStrategies::RestoreSubscriptionRunner.new(
184
183
  subscription: subscription,
185
184
  restart_terminator: restart_terminator,
186
- failed_subscription_notifier: failed_subscription_notifier,
185
+ failed_subscription_notifier: failed_subscription_notifier
187
186
  ),
188
187
  ]
189
188
  end
@@ -92,20 +92,20 @@ module PgEventstore
92
92
  id.hash
93
93
  end
94
94
 
95
- # @param another [Object]
95
+ # @param other [Object]
96
96
  # @return [Boolean]
97
- def eql?(another)
98
- return false unless another.is_a?(SubscriptionsSet)
97
+ def eql?(other)
98
+ return false unless other.is_a?(SubscriptionsSet)
99
99
 
100
- hash == another.hash
100
+ hash == other.hash
101
101
  end
102
102
 
103
- # @param another [Object]
103
+ # @param other [Object]
104
104
  # @return [Boolean]
105
- def ==(another)
106
- return false unless another.is_a?(SubscriptionsSet)
105
+ def ==(other)
106
+ return false unless other.is_a?(SubscriptionsSet)
107
107
 
108
- id == another.id
108
+ id == other.id
109
109
  end
110
110
 
111
111
  private
@@ -27,9 +27,11 @@ module PgEventstore
27
27
  end
28
28
 
29
29
  # @return [PgEventstore::SubscriptionsSet]
30
+ # rubocop:disable Naming/MemoizedInstanceVariableName
30
31
  def persisted_subscriptions_set
31
32
  @subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).create(@subscriptions_set_attrs)
32
33
  end
34
+ # rubocop:enable Naming/MemoizedInstanceVariableName
33
35
 
34
36
  # @return [void]
35
37
  def reset_subscriptions_set
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'monitor'
4
+
5
+ module PgEventstore
6
+ # @!visibility private
7
+ class SynchronizedArray < Array
8
+ include MonitorMixin
9
+
10
+ alias old_shift shift
11
+ alias old_unshift unshift
12
+ alias old_push push
13
+ alias old_clear clear
14
+ alias old_size size
15
+ alias old_empty? empty?
16
+
17
+ def shift(...)
18
+ synchronize { old_shift(...) }
19
+ end
20
+
21
+ def unshift(...)
22
+ synchronize { old_unshift(...) }
23
+ end
24
+
25
+ def push(...)
26
+ synchronize { old_push(...) }
27
+ end
28
+
29
+ def clear(...)
30
+ synchronize { old_clear(...) }
31
+ end
32
+
33
+ def size(...)
34
+ synchronize { old_size(...) }
35
+ end
36
+
37
+ def empty?(...)
38
+ synchronize { old_empty?(...) }
39
+ end
40
+ end
41
+ end
@@ -14,13 +14,13 @@ helpers = Class.new do
14
14
  end
15
15
 
16
16
  def db_name
17
- @db_name ||= URI.parse(ENV.fetch('PG_EVENTSTORE_URI')).path&.delete("/")
17
+ @db_name ||= URI.parse(ENV.fetch('PG_EVENTSTORE_URI')).path&.delete('/')
18
18
  end
19
19
  end
20
20
  end
21
21
 
22
22
  namespace :pg_eventstore do
23
- desc "Creates events table, indexes, etc."
23
+ desc 'Creates events table, indexes, etc.'
24
24
  task :create do
25
25
  PgEventstore.configure do |config|
26
26
  config.pg_uri = helpers.postgres_uri
@@ -28,7 +28,8 @@ namespace :pg_eventstore do
28
28
 
29
29
  PgEventstore.connection.with do |conn|
30
30
  exists =
31
- conn.exec_params("SELECT 1 as exists FROM pg_database where datname = $1", [helpers.db_name]).first&.dig('exists')
31
+ conn.exec_params('SELECT 1 as exists FROM pg_database where datname = $1', [helpers.db_name]).
32
+ first&.dig('exists')
32
33
  if exists
33
34
  puts "#{helpers.db_name} already exists. Skipping."
34
35
  else
@@ -42,7 +43,7 @@ namespace :pg_eventstore do
42
43
  config.pg_uri = ENV['PG_EVENTSTORE_URI']
43
44
  end
44
45
 
45
- migration_files_root = "#{Gem::Specification.find_by_name("pg_eventstore").gem_dir}/db/migrations"
46
+ migration_files_root = "#{Gem::Specification.find_by_name('pg_eventstore').gem_dir}/db/migrations"
46
47
 
47
48
  PgEventstore.connection.with do |conn|
48
49
  conn.exec('CREATE TABLE IF NOT EXISTS migrations (number int NOT NULL)')
@@ -50,7 +51,7 @@ namespace :pg_eventstore do
50
51
  conn.exec('SELECT number FROM migrations ORDER BY number DESC LIMIT 1').to_a.dig(0, 'number') || -1
51
52
 
52
53
  Dir.chdir migration_files_root do
53
- Dir["*.{sql,rb}"].sort_by { |f_name| f_name.split('_').first.to_i }.each do |f_name|
54
+ Dir['*.{sql,rb}'].sort_by { |f_name| f_name.split('_').first.to_i }.each do |f_name|
54
55
  number = File.basename(f_name).split('_')[0].to_i
55
56
  next if latest_migration >= number
56
57
 
@@ -65,7 +66,7 @@ namespace :pg_eventstore do
65
66
  end
66
67
  end
67
68
 
68
- desc "Drops events table and related pg_eventstore objects."
69
+ desc 'Drops events table and related pg_eventstore objects.'
69
70
  task :drop do
70
71
  PgEventstore.configure do |config|
71
72
  config.pg_uri = helpers.postgres_uri
@@ -50,7 +50,7 @@ module PgEventstore
50
50
  {
51
51
  class: original_error.class,
52
52
  message: original_error.message,
53
- backtrace: original_error.backtrace
53
+ backtrace: original_error.backtrace,
54
54
  }.tap do |attrs|
55
55
  attrs.merge!(error.extra) if error.is_a?(WrappedException)
56
56
  end
@@ -61,7 +61,7 @@ module PgEventstore
61
61
  def underscore_str(str)
62
62
  str = str.dup
63
63
  str[0] = str[0].downcase
64
- str.gsub!(/[A-Z]/) { |letter| '_' + letter.downcase }
64
+ str.gsub!(/[A-Z]/) { |letter| "_#{letter.downcase}" }
65
65
  str
66
66
  end
67
67
 
@@ -83,7 +83,7 @@ module PgEventstore
83
83
  # @param content [String]
84
84
  # @return [void]
85
85
  def write_to_file(file_path, content)
86
- file = File.open(file_path, "w")
86
+ file = File.open(file_path, 'w')
87
87
  file.write(content)
88
88
  ensure
89
89
  file&.close
@@ -91,20 +91,24 @@ module PgEventstore
91
91
 
92
92
  # @param file_path [String]
93
93
  # @return [void]
94
+ # rubocop:disable Lint/SuppressedException
94
95
  def remove_file(file_path)
95
96
  File.delete(file_path)
96
97
  rescue Errno::ENOENT
97
98
  end
99
+ # rubocop:enable Lint/SuppressedException
98
100
 
99
101
  # @param file_path [String]
100
102
  # @return [String, nil]
103
+ # rubocop:disable Lint/SuppressedException
101
104
  def read_pid(file_path)
102
- file = File.open(file_path, "r")
105
+ file = File.open(file_path, 'r')
103
106
  file.readline.strip
104
107
  rescue Errno::ENOENT
105
108
  ensure
106
109
  file&.close
107
110
  end
111
+ # rubocop:enable Lint/SuppressedException
108
112
 
109
113
  # @param exception [StandardError]
110
114
  # @param extra [Hash] additional exception info
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PgEventstore
4
4
  # @return [String]
5
- VERSION = "1.13.1"
5
+ VERSION = '1.13.3'
6
6
  end
@@ -13,26 +13,33 @@ module PgEventstore
13
13
  COOKIES_CONFIG_KEY = 'current_config'
14
14
  # @return [String]
15
15
  COOKIES_FLASH_MESSAGE_KEY = 'flash_message'
16
+ # @return [Array<Symbol>]
17
+ LOGGING_ENVS = %i[development test].freeze
18
+ private_constant :LOGGING_ENVS
16
19
 
17
20
  # Defines a replacement for empty string value in a stream attributes filter or in an event type filter. This
18
21
  # replacement is needed to differentiate a user selection vs default placeholder value.
19
22
  # @return [String]
20
- EMPTY_STRING_SIGN = "\x00".freeze
23
+ EMPTY_STRING_SIGN = "\x00"
21
24
 
22
- set :static_cache_control, [:private, max_age: 86400]
25
+ set :static_cache_control, [:private, { max_age: 86_400 }]
23
26
  set :environment, -> { (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['APP_ENV'])&.to_sym || :development }
24
- set :logging, -> { environment == :development || environment == :test }
27
+ set :logging, -> { LOGGING_ENVS.include?(environment) }
25
28
  set :erb, layout: :'layouts/application'
26
29
  set :host_authorization, { allow_if: ->(_env) { true } }
27
30
 
28
31
  helpers(Paginator::Helpers, Subscriptions::Helpers) do
29
32
  # @return [Array<Hash>, nil]
33
+ # rubocop:disable Style/HashConversion
30
34
  def streams_filter
31
35
  streams = QueryBuilders::EventsFiltering.extract_streams_filter(params)
32
- streams&.select { _1 in { context: String, stream_name: String, stream_id: String } }&.map do
33
- Hash[_1.reject { |_, value| value == '' }].transform_keys(&:to_sym)
34
- end&.reject { _1.empty? }
36
+ streams = streams.select { _1 in { context: String, stream_name: String, stream_id: String } }
37
+ streams = streams.map do |stream_attrs|
38
+ Hash[stream_attrs.reject { |_, value| value == '' }].transform_keys(&:to_sym)
39
+ end
40
+ streams.reject(&:empty?)
35
41
  end
42
+ # rubocop:enable Style/HashConversion
36
43
 
37
44
  # @return [String, nil]
38
45
  def system_stream
@@ -44,7 +51,7 @@ module PgEventstore
44
51
  def events_filter
45
52
  event_filters = { filter: { event_types: params.dig(:filter, :events) } }
46
53
  events = QueryBuilders::EventsFiltering.extract_event_types_filter(event_filters)
47
- events&.reject { _1 == '' }
54
+ events.reject { _1 == '' }
48
55
  end
49
56
 
50
57
  # @return [Symbol]
@@ -81,7 +88,7 @@ module PgEventstore
81
88
  end
82
89
  halt 200, {
83
90
  results: results,
84
- pagination: { more: !collection.next_page_starting_id.nil?, starting_id: collection.next_page_starting_id }
91
+ pagination: { more: !collection.next_page_starting_id.nil?, starting_id: collection.next_page_starting_id },
85
92
  }.to_json
86
93
  end
87
94
 
@@ -144,7 +151,7 @@ module PgEventstore
144
151
  order: Paginator::EventsCollection::SQL_DIRECTIONS[params[:order]],
145
152
  options: {
146
153
  filter: { event_types: events_filter, streams: streams_filter },
147
- resolve_link_tos: resolve_link_tos?
154
+ resolve_link_tos: resolve_link_tos?,
148
155
  },
149
156
  system_stream: system_stream
150
157
  )
@@ -154,7 +161,7 @@ module PgEventstore
154
161
  halt 200, {
155
162
  events: erb(:'home/partials/events', { layout: false }, { events: @collection.collection }),
156
163
  total_count: total_count(@collection.total_count),
157
- pagination: erb(:'home/partials/pagination_links', { layout: false }, { collection: @collection })
164
+ pagination: erb(:'home/partials/pagination_links', { layout: false }, { collection: @collection }),
158
165
  }.to_json
159
166
  else
160
167
  erb :'home/dashboard'
@@ -223,7 +230,7 @@ module PgEventstore
223
230
  options: {
224
231
  query: params[:term],
225
232
  context: unescape_empty_string(params[:context]),
226
- stream_name: unescape_empty_string(params[:stream_name])
233
+ stream_name: unescape_empty_string(params[:stream_name]),
227
234
  }
228
235
  )
229
236
  paginated_json_response(collection)
@@ -297,7 +304,7 @@ module PgEventstore
297
304
  PgEventstore.maintenance(current_config).delete_event(event, force: force)
298
305
  self.flash_message = {
299
306
  message: "An event at global position #{event.global_position} has been deleted successfully.",
300
- kind: 'success'
307
+ kind: 'success',
301
308
  }
302
309
  rescue TooManyRecordsToLockError => e
303
310
  text = <<~TEXT
@@ -319,10 +326,10 @@ module PgEventstore
319
326
  stream_id: params[:stream_id]&.to_s,
320
327
  }
321
328
 
322
- err_message = ->(attrs) {
329
+ err_message = lambda { |attrs|
323
330
  self.flash_message = {
324
331
  message: "Could not delete #{attrs}. It is not valid stream for deletion.",
325
- kind: 'error'
332
+ kind: 'error',
326
333
  }
327
334
  }
328
335
 
@@ -334,7 +341,7 @@ module PgEventstore
334
341
  PgEventstore.maintenance(current_config).delete_stream(stream)
335
342
  self.flash_message = {
336
343
  message: "Stream #{stream.to_hash} has been successfully deleted.",
337
- kind: 'success'
344
+ kind: 'success',
338
345
  }
339
346
  end
340
347
  else
@@ -9,14 +9,13 @@ module PgEventstore
9
9
 
10
10
  # @return [Array<Hash<String => String>>]
11
11
  def collection
12
- @_collection ||=
12
+ @collection ||=
13
13
  begin
14
- sql_builder =
15
- SQLBuilder.new.select('event_type').from('partitions').
16
- where('context is not null and stream_name is not null').
17
- group('event_type').order("event_type #{order}").limit(per_page)
14
+ sql_builder = SQLBuilder.new.select('event_type').from('partitions')
15
+ sql_builder.where('context is not null and stream_name is not null')
18
16
  sql_builder.where("event_type #{direction_operator} ?", starting_id) if starting_id
19
17
  sql_builder.where('event_type ilike ?', "%#{options[:query]}%")
18
+ sql_builder.group('event_type').order("event_type #{order}").limit(per_page)
20
19
  connection.with do |conn|
21
20
  conn.exec_params(*sql_builder.to_exec_params)
22
21
  end.to_a
@@ -28,12 +27,11 @@ module PgEventstore
28
27
  return unless collection.size == per_page
29
28
 
30
29
  starting_id = collection.first['event_type']
31
- sql_builder =
32
- SQLBuilder.new.select('event_type').from('partitions').
33
- where('context is not null and stream_name is not null').
34
- where("event_type #{direction_operator} ?", starting_id).
35
- where('event_type ilike ?', "%#{options[:query]}%").
36
- group('event_type').order("event_type #{order}").limit(1).offset(per_page)
30
+ sql_builder = SQLBuilder.new.select('event_type').from('partitions')
31
+ sql_builder.where('context is not null and stream_name is not null')
32
+ sql_builder.where("event_type #{direction_operator} ?", starting_id)
33
+ sql_builder.where('event_type ilike ?', "%#{options[:query]}%")
34
+ sql_builder.group('event_type').order("event_type #{order}").limit(1).offset(per_page)
37
35
 
38
36
  connection.with do |conn|
39
37
  conn.exec_params(*sql_builder.to_exec_params)
@@ -7,7 +7,7 @@ module PgEventstore
7
7
  # @return [Hash<String => Symbol>] SQL directions, string-to-symbol mapping
8
8
  SQL_DIRECTIONS = {
9
9
  'asc' => :asc,
10
- 'desc' => :desc
10
+ 'desc' => :desc,
11
11
  }.tap do |directions|
12
12
  directions.default = :desc
13
13
  end.freeze
@@ -32,7 +32,7 @@ module PgEventstore
32
32
 
33
33
  # @return [Array<PgEventstore::Event>]
34
34
  def collection
35
- @_collection ||= PgEventstore.client(config_name).read(
35
+ @collection ||= PgEventstore.client(config_name).read(
36
36
  @stream,
37
37
  options: options.merge(from_position: starting_id, max_count: per_page, direction: order)
38
38
  )
@@ -59,18 +59,17 @@ module PgEventstore
59
59
  ).to_sql_builder.unselect.select('global_position').offset(1)
60
60
  sql, params = sql_builder.to_exec_params
61
61
  sql = "SELECT * FROM (#{sql}) events ORDER BY global_position #{order} LIMIT 1"
62
- connection.with do |conn|
62
+ connection.with do |conn|
63
63
  conn.exec_params(sql, params)
64
64
  end.to_a.dig(0, 'global_position')
65
65
  end
66
66
 
67
67
  # @return [Integer]
68
68
  def total_count
69
- @_total_count ||=
69
+ @total_count ||=
70
70
  begin
71
- sql_builder =
72
- QueryBuilders::EventsFiltering.events_filtering(@stream, options).
73
- to_sql_builder.remove_limit.remove_group.remove_order
71
+ sql_builder = QueryBuilders::EventsFiltering.events_filtering(@stream, options).to_sql_builder
72
+ sql_builder.remove_limit.remove_group.remove_order
74
73
  count = estimate_count(sql_builder)
75
74
  return count if count > MAX_NUMBER_TO_COUNT
76
75