pg_eventstore 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +4 -0
- data/docs/subscriptions.md +14 -56
- data/exe/pg-eventstore +16 -0
- data/lib/pg_eventstore/callbacks.rb +16 -0
- data/lib/pg_eventstore/cli/commands/base_command.rb +45 -0
- data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +38 -0
- data/lib/pg_eventstore/cli/commands/help_command.rb +22 -0
- data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +96 -0
- data/lib/pg_eventstore/cli/commands/stop_subscriptions_command.rb +22 -0
- data/lib/pg_eventstore/cli/commands.rb +6 -0
- data/lib/pg_eventstore/cli/exit_codes.rb +12 -0
- data/lib/pg_eventstore/cli/parser_options/base_options.rb +46 -0
- data/lib/pg_eventstore/cli/parser_options/default_options.rb +10 -0
- data/lib/pg_eventstore/cli/parser_options/metadata.rb +34 -0
- data/lib/pg_eventstore/cli/parser_options/subscription_options.rb +31 -0
- data/lib/pg_eventstore/cli/parser_options.rb +6 -0
- data/lib/pg_eventstore/cli/parsers/base_parser.rb +33 -0
- data/lib/pg_eventstore/cli/parsers/default_parser.rb +24 -0
- data/lib/pg_eventstore/cli/parsers/subscription_parser.rb +24 -0
- data/lib/pg_eventstore/cli/parsers.rb +5 -0
- data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +75 -0
- data/lib/pg_eventstore/cli/try_unlock_subscriptions_set.rb +16 -0
- data/lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb +67 -0
- data/lib/pg_eventstore/cli.rb +42 -0
- data/lib/pg_eventstore/extensions/options_extension.rb +53 -8
- data/lib/pg_eventstore/rspec/has_option_matcher.rb +38 -13
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +13 -1
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +1 -1
- data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +1 -1
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +3 -1
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +10 -50
- data/lib/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rb +22 -0
- data/lib/pg_eventstore/subscriptions/subscription_feeder_commands.rb +1 -0
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +7 -2
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +44 -10
- data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +1 -1
- data/lib/pg_eventstore/utils.rb +32 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +1 -1
- data/lib/pg_eventstore/web/views/home/partials/events.erb +5 -5
- data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +3 -3
- data/lib/pg_eventstore/web/views/layouts/application.erb +3 -3
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +6 -6
- data/lib/pg_eventstore.rb +5 -2
- data/sig/pg_eventstore/callbacks.rbs +4 -0
- data/sig/pg_eventstore/cli/commands/base_command.rbs +13 -0
- data/sig/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rbs +14 -0
- data/sig/pg_eventstore/cli/commands/help_command.rbs +13 -0
- data/sig/pg_eventstore/cli/commands/start_subscriptions_command.rbs +28 -0
- data/sig/pg_eventstore/cli/commands/stop_subscriptions_command.rbs +11 -0
- data/sig/pg_eventstore/cli/exit_codes.rbs +8 -0
- data/sig/pg_eventstore/cli/parser_options/base_options.rbs +15 -0
- data/sig/pg_eventstore/cli/parser_options/default_options.rbs +8 -0
- data/sig/pg_eventstore/cli/parser_options/metadata.rbs +11 -0
- data/sig/pg_eventstore/cli/parser_options/subscription_options.rbs +9 -0
- data/sig/pg_eventstore/cli/parsers/base_parser.rbs +18 -0
- data/sig/pg_eventstore/cli/parsers/default_parser.rbs +9 -0
- data/sig/pg_eventstore/cli/parsers/subscription_parser.rbs +9 -0
- data/sig/pg_eventstore/cli/try_to_delete_subscriptions_set.rbs +24 -0
- data/sig/pg_eventstore/cli/try_unlock_subscriptions_set.rbs +7 -0
- data/sig/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rbs +26 -0
- data/sig/pg_eventstore/cli.rbs +10 -0
- data/sig/pg_eventstore/extensions/options_extension.rbs +18 -1
- data/sig/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rbs +8 -0
- data/sig/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rbs +7 -1
- data/sig/pg_eventstore/subscriptions/queries/subscription_command_queries.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +10 -11
- data/sig/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +13 -1
- data/sig/pg_eventstore/utils.rbs +2 -0
- data/sig/pg_eventstore.rbs +2 -8
- metadata +44 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 337db5e3df424d6d72b3e08367a730af7c77ebfd907d38b309309e30a563e81e
|
4
|
+
data.tar.gz: 71c36ea38c4d756adea73939ab15e75a2b4500d17768416e0f37013160f0b696
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70699c2d05ddb69f64922c6e0e67d46a523b5ed2f70d9278134435a144bc002013fe0657a408a0657ccb9f0fe2d1f9cd7a1521a50e128375b1c1d8ec1e127584
|
7
|
+
data.tar.gz: c1125f8dc9af55a1ce1752b94a9bb3273e1153c70e726845867bb91cad9a4b407bcc63537d2bb5859bef20508ce5bfb4391ed56218a690bef2119cf1351597d7
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.6.0]
|
4
|
+
- Introduce subscriptions CLI. Type `pg-eventstore subscriptions --help` to see available commands. The main purpose of it is to provide the single way to start/stop subscription processes. Check [Subscriptions](docs/subscriptions.md#creating-a-subscription) docs about the new way to start and keep running a subscriptions process.
|
5
|
+
|
3
6
|
## [1.5.0]
|
4
7
|
- Add ability to toggle link events in the admin UI
|
5
8
|
- Mark linked events in the admin UI with "link" icon
|
data/README.md
CHANGED
@@ -49,6 +49,10 @@ Documentation chapters:
|
|
49
49
|
- [How to make multiple commands atomic](docs/multiple_commands.md)
|
50
50
|
- [Admin UI](docs/admin_ui.md)
|
51
51
|
|
52
|
+
## CLI
|
53
|
+
|
54
|
+
The gem is shipped with its own CLI. Use `pg-eventstore --help` to find out its capabilities.
|
55
|
+
|
52
56
|
## Development
|
53
57
|
|
54
58
|
After checking out the repo, run:
|
data/docs/subscriptions.md
CHANGED
@@ -29,7 +29,7 @@ Now we can use the `#subscribe` method to create the subscription:
|
|
29
29
|
subscriptions_manager.subscribe('MyAwesomeSubscription', handler: proc { |event| puts event })
|
30
30
|
```
|
31
31
|
|
32
|
-
First argument is the subscription's name. **It must be unique within the
|
32
|
+
First argument is the subscription's name. **It must be unique within the subscriptions set**. Second argument is your subscription's handler where you will be processing your events as they arrive. The example shows the minimum set of arguments required to create the subscription.
|
33
33
|
|
34
34
|
In the given state it will be listening to all events from all streams. You can define various filters by providing the `:filter` key of `options` argument:
|
35
35
|
|
@@ -50,44 +50,25 @@ subscriptions_manager.start
|
|
50
50
|
# => PgEventstore::BasicRunner
|
51
51
|
```
|
52
52
|
|
53
|
-
After calling `#start` all subscriptions are locked behind the given subscriptions set and can't be locked by any other subscriptions set. This measure is needed to prevent running the same subscription under the same subscription set using different processes/subscription managers. Such situation will lead to a malformed subscription state and will break its position, meaning the same event will be processed several times.
|
53
|
+
After calling `#start` all subscriptions are locked behind the given subscriptions set and can't be locked by any other subscriptions set. This measure is needed to prevent running the same subscription under the same subscription set using different processes/subscription managers. Such situation will lead to a malformed subscription state and will break its position, meaning the same event will be processed several times.
|
54
54
|
|
55
|
-
|
56
|
-
timeout = 20 # 20 seconds
|
57
|
-
deadline = Time.now + timeout
|
58
|
-
loop do
|
59
|
-
break if subscriptions_manager.start
|
60
|
-
if Time.now > deadline
|
61
|
-
puts "Failed to acquire subscriptions lock within #{timeout} seconds. Exiting now."
|
62
|
-
exit
|
63
|
-
end
|
64
|
-
sleep 2
|
65
|
-
end
|
66
|
-
```
|
67
|
-
|
68
|
-
To "unlock" the subscription you should gracefully stop the subscription manager:
|
69
|
-
|
70
|
-
```ruby
|
71
|
-
subscriptions_manager.stop
|
72
|
-
```
|
73
|
-
|
74
|
-
If you shut down the process which runs your subscriptions without calling the `#stop` method, subscriptions will remain locked, and the only way to unlock them will be to call the `#force_lock!` method before calling the `#start` method:
|
55
|
+
If, for some reason, you want to lock already locked subscription - you can provide `force_lock: true`:
|
75
56
|
|
76
57
|
```ruby
|
77
|
-
subscriptions_manager.force_lock
|
58
|
+
subscriptions_manager = PgEventstore.subscriptions_manager(subscription_set: 'SubscriptionsOfMyAwesomeMicroservice', force_lock: true)
|
78
59
|
subscriptions_manager.start
|
79
60
|
```
|
80
61
|
|
81
62
|
A complete example of the subscription setup process looks like this:
|
82
63
|
|
83
64
|
```ruby
|
84
|
-
require 'pg_eventstore'
|
85
|
-
|
86
65
|
PgEventstore.configure do |config|
|
87
66
|
config.pg_uri = ENV.fetch('PG_EVENTSTORE_URI') { 'postgresql://postgres:postgres@localhost:5532/eventstore' }
|
88
67
|
end
|
89
68
|
|
90
|
-
subscriptions_manager = PgEventstore.subscriptions_manager(
|
69
|
+
subscriptions_manager = PgEventstore.subscriptions_manager(
|
70
|
+
subscription_set: 'MyAwesomeSubscriptions'
|
71
|
+
)
|
91
72
|
subscriptions_manager.subscribe(
|
92
73
|
'Foo events Subscription',
|
93
74
|
handler: proc { |event| p "Foo events Subscription: #{event.inspect}" },
|
@@ -99,40 +80,17 @@ subscriptions_manager.subscribe(
|
|
99
80
|
options: { filter: { streams: [{ context: 'BarCtx' }] }
|
100
81
|
}
|
101
82
|
)
|
102
|
-
subscriptions_manager.
|
103
|
-
|
104
|
-
deadline = Time.now + timeout
|
105
|
-
loop do
|
106
|
-
break if subscriptions_manager.start
|
107
|
-
if Time.now > deadline
|
108
|
-
puts "Failed to acquire subscriptions lock within #{timeout} seconds. Exiting now."
|
109
|
-
exit
|
110
|
-
end
|
111
|
-
sleep 2
|
112
|
-
end
|
83
|
+
subscriptions_manager.start
|
84
|
+
```
|
113
85
|
|
114
|
-
|
115
|
-
puts "Received TERM signal. Stopping Subscriptions Manager and exiting..."
|
116
|
-
# It is important to wrap subscriptions_manager.stop into another Thread, because it uses Thread::Mutex#synchronize
|
117
|
-
# internally, but its usage is not allowed inside Kernel.trap block
|
118
|
-
Thread.new { subscriptions_manager.stop }.join
|
119
|
-
exit
|
120
|
-
end
|
86
|
+
Persist this script into a file(let's say `subscriptions.rb`). Now it is time to start the process which will be processing those subscriptions. `pg_eventstore` has CLI for that purpose:
|
121
87
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
puts <<~TEXT
|
126
|
-
Subscription <<#{subscription.name.inspect}>> is at position #{subscription.current_position}. \
|
127
|
-
Events processed: #{subscription.total_processed_events}
|
128
|
-
TEXT
|
129
|
-
end
|
130
|
-
puts "Current SubscriptionsSet: #{subscriptions_manager.subscriptions_set}"
|
131
|
-
puts ""
|
132
|
-
end
|
88
|
+
```bash
|
89
|
+
# -r ./subscriptions.rb will load our subscriptions definitions
|
90
|
+
pg-eventstore subscriptions start -r ./subscriptions.rb
|
133
91
|
```
|
134
92
|
|
135
|
-
|
93
|
+
After running that test subscriptions you can open another ruby console and test posting different events:
|
136
94
|
|
137
95
|
```ruby
|
138
96
|
require 'pg_eventstore'
|
data/exe/pg-eventstore
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "pg_eventstore"
|
5
|
+
require "pg_eventstore/cli"
|
6
|
+
require "logger"
|
7
|
+
|
8
|
+
logger = Logger.new(STDOUT)
|
9
|
+
logger.level = :info
|
10
|
+
logger.progname = "pg_eventstore"
|
11
|
+
logger.formatter = proc do |severity, time, progname, msg|
|
12
|
+
"\e[36m#{progname} | \e[0m#{time.utc.strftime("%FT%TZ")} #{severity}: #{msg}\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
PgEventstore.logger = logger
|
16
|
+
Kernel.exit(PgEventstore::CLI.execute(ARGV))
|
@@ -95,6 +95,22 @@ module PgEventstore
|
|
95
95
|
result
|
96
96
|
end
|
97
97
|
|
98
|
+
# @param action [Object]
|
99
|
+
# @param filter [Symbol]
|
100
|
+
# @param callback [#call]
|
101
|
+
# @return [void]
|
102
|
+
def remove_callback(action, filter, callback)
|
103
|
+
return unless @callbacks.dig(action, filter)
|
104
|
+
|
105
|
+
@callbacks[action][filter].delete(callback)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Clear all defined callbacks
|
109
|
+
# @return [void]
|
110
|
+
def clear
|
111
|
+
@callbacks.clear
|
112
|
+
end
|
113
|
+
|
98
114
|
private
|
99
115
|
|
100
116
|
# @return [void]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
class BaseCommand
|
7
|
+
module BaseCommandActions
|
8
|
+
# @return [Integer] exit code
|
9
|
+
def call
|
10
|
+
load_external_files
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# @return [void]
|
17
|
+
def load_external_files
|
18
|
+
options.requires.each do |file_path|
|
19
|
+
require(file_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def inherited(klass)
|
26
|
+
super
|
27
|
+
klass.prepend BaseCommandActions
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :options
|
32
|
+
|
33
|
+
# @param options [PgEventstore::CLI::ParserOptions::BaseOptions]
|
34
|
+
def initialize(options)
|
35
|
+
@options = options
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Integer] exit code
|
39
|
+
def call
|
40
|
+
raise NotImplementedError
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
module CallbackHandlers
|
7
|
+
class StartCmdHandlers
|
8
|
+
include Extensions::CallbackHandlersExtension
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# @param subscription_managers [Set<PgEventstore::SubscriptionsManager>]
|
12
|
+
# @param manager [PgEventstore::SubscriptionsManager]
|
13
|
+
# @return [void]
|
14
|
+
def register_managers(subscription_managers, manager)
|
15
|
+
subscription_managers.add(manager)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param action [Proc]
|
19
|
+
# @param manager [PgEventstore::SubscriptionsManager]
|
20
|
+
# @return [void]
|
21
|
+
def handle_start_up(action, manager)
|
22
|
+
action.call
|
23
|
+
rescue SubscriptionAlreadyLockedError => error
|
24
|
+
PgEventstore.logger&.error(
|
25
|
+
<<~TEXT
|
26
|
+
Subscription #{error.name.inspect} from #{error.set.inspect} set is locked under \
|
27
|
+
SubscriptionsSet##{error.lock_id}. Trying to unlock...
|
28
|
+
TEXT
|
29
|
+
)
|
30
|
+
raise unless TryUnlockSubscriptionsSet.try_unlock(manager.config_name, error.lock_id)
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
class HelpCommand
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
# @param options [PgEventstore::CLI::ParserOptions::BaseOptions]
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Integer] exit code
|
15
|
+
def call
|
16
|
+
puts options.help
|
17
|
+
ExitCodes::SUCCESS
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'callback_handlers/start_cmd_handlers'
|
4
|
+
|
5
|
+
module PgEventstore
|
6
|
+
module CLI
|
7
|
+
module Commands
|
8
|
+
class StartSubscriptionsCommand < BaseCommand
|
9
|
+
# @return [Integer] seconds
|
10
|
+
KEEP_ALIVE_INTERVAL = 2
|
11
|
+
|
12
|
+
def initialize(...)
|
13
|
+
super
|
14
|
+
@subscription_managers = Set.new
|
15
|
+
@running = false
|
16
|
+
attach_callbacks
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] exit code
|
20
|
+
def call
|
21
|
+
return ExitCodes::ERROR unless running_subscriptions?
|
22
|
+
|
23
|
+
@running = true
|
24
|
+
setup_killsig
|
25
|
+
persist_pid
|
26
|
+
keep_process_alive
|
27
|
+
ExitCodes::SUCCESS
|
28
|
+
rescue SubscriptionAlreadyLockedError => error
|
29
|
+
PgEventstore.logger&.error(
|
30
|
+
"SubscriptionsSet##{error.lock_id} is still there. Are you stopping it at all?"
|
31
|
+
)
|
32
|
+
ExitCodes::ERROR
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# @return [Boolean]
|
38
|
+
def running_subscriptions?
|
39
|
+
return true if @subscription_managers.any?(&:running?)
|
40
|
+
|
41
|
+
PgEventstore.logger&.warn("No subscriptions start ups were detected. Existing...")
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [void]
|
46
|
+
def setup_killsig
|
47
|
+
Kernel.trap('TERM') do
|
48
|
+
Thread.new do
|
49
|
+
PgEventstore.logger&.info("Received TERM signal, stopping subscriptions and exiting...")
|
50
|
+
end.join
|
51
|
+
# Because the implementation uses Mutex - wrap it into Thread to bypass the limitations of Kernel#trap
|
52
|
+
@subscription_managers.map do |manager|
|
53
|
+
Thread.new do
|
54
|
+
# Initiate graceful shutdown
|
55
|
+
manager.stop
|
56
|
+
end
|
57
|
+
end.each(&:join)
|
58
|
+
Utils.remove_file(options.pid_path)
|
59
|
+
@running = false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [void]
|
64
|
+
def persist_pid
|
65
|
+
Utils.write_to_file(options.pid_path, Process.pid.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [void]
|
69
|
+
def keep_process_alive
|
70
|
+
PgEventstore.logger&.info("Startup is successful. Processing subscriptions...")
|
71
|
+
loop do
|
72
|
+
# SubscriptionsManager#subscriptions_set becomes nil when everything gets stopped.
|
73
|
+
if @subscription_managers.all? { |manager| manager.subscriptions_set.nil? }
|
74
|
+
PgEventstore.logger&.info("All subscriptions were gracefully shut down. Exiting now...")
|
75
|
+
break
|
76
|
+
end
|
77
|
+
break unless @running
|
78
|
+
sleep KEEP_ALIVE_INTERVAL
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [void]
|
83
|
+
def attach_callbacks
|
84
|
+
CLI.callbacks.define_callback(
|
85
|
+
:start_manager, :before,
|
86
|
+
CallbackHandlers::StartCmdHandlers.setup_handler(:register_managers, @subscription_managers)
|
87
|
+
)
|
88
|
+
CLI.callbacks.define_callback(
|
89
|
+
:start_manager, :around,
|
90
|
+
CallbackHandlers::StartCmdHandlers.setup_handler(:handle_start_up)
|
91
|
+
)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
class StopSubscriptionsCommand < BaseCommand
|
7
|
+
# @return [Integer] exit code
|
8
|
+
def call
|
9
|
+
pid = Utils.read_pid(options.pid_path)&.to_i
|
10
|
+
if pid && pid > 0
|
11
|
+
PgEventstore.logger&.info("Stopping process #{pid}.")
|
12
|
+
Process.kill('TERM', pid)
|
13
|
+
return ExitCodes::SUCCESS
|
14
|
+
end
|
15
|
+
|
16
|
+
PgEventstore.logger&.error("Pid file #{options.pid_path.inspect} does not exist or empty.")
|
17
|
+
ExitCodes::ERROR
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module ParserOptions
|
6
|
+
class BaseOptions
|
7
|
+
include Extensions::OptionsExtension
|
8
|
+
|
9
|
+
option(:help, metadata: Metadata.new(short: '-h', long: '--help', description: 'Prints this help'))
|
10
|
+
option(
|
11
|
+
:requires,
|
12
|
+
metadata: Metadata.new(
|
13
|
+
short: '-rFILE_PATH',
|
14
|
+
long: '--require=FILE_PATH',
|
15
|
+
description: 'Ruby files to load. You can provide this option multiple times to load more files.'
|
16
|
+
)
|
17
|
+
) do
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param parser [OptionParser]
|
22
|
+
# @return [void]
|
23
|
+
def attach_parser_handlers(parser)
|
24
|
+
parser.on(*to_parser_opts(:help)) do
|
25
|
+
self.help = parser.to_s
|
26
|
+
end
|
27
|
+
parser.on(*to_parser_opts(:requires)) do |path|
|
28
|
+
requires.push(path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param option [Symbol]
|
33
|
+
# @return [Array<String>]
|
34
|
+
def to_parser_opts(option)
|
35
|
+
option(option).metadata.to_parser_opts
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param option [Symbol]
|
39
|
+
# @return [PgEventstore::Extensions::OptionsExtension::Option]
|
40
|
+
def option(option)
|
41
|
+
self.class.options[option]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module ParserOptions
|
6
|
+
class Metadata
|
7
|
+
include Extensions::OptionsExtension
|
8
|
+
|
9
|
+
option(:short)
|
10
|
+
option(:long)
|
11
|
+
option(:description)
|
12
|
+
|
13
|
+
# @return [Array<String>]
|
14
|
+
def to_parser_opts
|
15
|
+
[short, long, description]
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Integer]
|
19
|
+
def hash
|
20
|
+
to_parser_opts.hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param another [Object]
|
24
|
+
# @return [Boolean]
|
25
|
+
def ==(another)
|
26
|
+
return false unless another.is_a?(Metadata)
|
27
|
+
|
28
|
+
to_parser_opts == another.to_parser_opts
|
29
|
+
end
|
30
|
+
alias eql? ==
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module ParserOptions
|
6
|
+
class SubscriptionOptions < BaseOptions
|
7
|
+
option(
|
8
|
+
:pid_path,
|
9
|
+
metadata: Metadata.new(
|
10
|
+
short: '-pFILE_PATH',
|
11
|
+
long: '--pid=FILE_PATH',
|
12
|
+
description: 'Defines pid file path. Defaults to /tmp/pg-es_subscriptions.pid'
|
13
|
+
)
|
14
|
+
) do
|
15
|
+
'/tmp/pg-es_subscriptions.pid'
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param parser [OptionParser]
|
19
|
+
# @return [void]
|
20
|
+
def attach_parser_handlers(parser)
|
21
|
+
super
|
22
|
+
%i[pid_path].each do |option|
|
23
|
+
parser.on(*to_parser_opts(option)) do |value|
|
24
|
+
public_send("#{option}=", value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Parsers
|
6
|
+
class BaseParser
|
7
|
+
class << self
|
8
|
+
# @return [String]
|
9
|
+
def banner
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :args, :options
|
15
|
+
|
16
|
+
# @param args [Array<String>]
|
17
|
+
# @param options [PgEventstore::CLI::ParserOptions::BaseOptions]
|
18
|
+
def initialize(args, options)
|
19
|
+
@args = args
|
20
|
+
@options = options
|
21
|
+
@parser = ::OptionParser.new(self.class.banner)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Array<Array<String>, PgEventstore::CLI::ParserOptions::BaseOptions>] list of commands and parsed
|
25
|
+
# options
|
26
|
+
def parse
|
27
|
+
@options.attach_parser_handlers(@parser)
|
28
|
+
[@parser.parse(args), @options]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Parsers
|
6
|
+
class DefaultParser < BaseParser
|
7
|
+
class << self
|
8
|
+
# @return [String]
|
9
|
+
def banner
|
10
|
+
<<~TEXT
|
11
|
+
Usage: pg-eventstore [options]
|
12
|
+
pg-eventstore [command]
|
13
|
+
|
14
|
+
Commands:
|
15
|
+
subscriptions Start, stop subscriptions
|
16
|
+
|
17
|
+
Options:
|
18
|
+
TEXT
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
module Parsers
|
6
|
+
class SubscriptionParser < BaseParser
|
7
|
+
class << self
|
8
|
+
# @return [String]
|
9
|
+
def banner
|
10
|
+
<<~TEXT
|
11
|
+
Usage: pg-eventstore subscriptions [command] [options]
|
12
|
+
|
13
|
+
Commands:
|
14
|
+
start Start subscriptions. Example: pg-eventstore subscriptions start -r lib/my_subscriptions.rb
|
15
|
+
stop Stop subscriptions. Example: pg-eventstore subscriptions stop
|
16
|
+
|
17
|
+
Options:
|
18
|
+
TEXT
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|