edgycircle_kommando 1.0.2
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 +7 -0
- data/db/migrations/1.down.sql +2 -0
- data/db/migrations/1.up.sql +12 -0
- data/lib/kommando.rb +12 -0
- data/lib/kommando/active_record.rb +9 -0
- data/lib/kommando/command.rb +27 -0
- data/lib/kommando/command_plugins/auto_schedule.rb +15 -0
- data/lib/kommando/command_plugins/base.rb +17 -0
- data/lib/kommando/command_plugins/execute.rb +79 -0
- data/lib/kommando/command_plugins/schedule.rb +105 -0
- data/lib/kommando/command_plugins/validate.rb +52 -0
- data/lib/kommando/command_result.rb +68 -0
- data/lib/kommando/scheduled_command_adapters/active_record.rb +52 -0
- data/lib/kommando/scheduled_command_runner.rb +25 -0
- data/lib/kommando/scheduled_command_worker.rb +23 -0
- data/lib/kommando/version.rb +3 -0
- metadata +142 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e2962518e6deb43c8cd51657f311ebbd96caf84d0e04cfb7505c7c427fddec0b
|
|
4
|
+
data.tar.gz: 8cdfe6722fb04875ee8fc3e0fd1240d8c6edbae8dbddb992cc3a9fa89c4c6166
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 895e1f7f17a43f5b9ec5d8fe05a71a09d3b024c55c639f4fcd4c0916597502f0ff23caf352f9001c6d3e45b6b959f134fa590576e83a24383dfc2cf53f9f6434
|
|
7
|
+
data.tar.gz: cdb250ff046a059b5aaf59f7fcc2384ff8174e9fbdde943826d09280403f41f4bbd9f5ec7127158b5dec014f1128dfd01c7e7e93afcd11ef4004bc4e602722f2
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE kommando_scheduled_commands (
|
|
2
|
+
id uuid NOT NULL,
|
|
3
|
+
name character varying NOT NULL,
|
|
4
|
+
parameters json NOT NULL,
|
|
5
|
+
handle_at timestamp without time zone NOT NULL,
|
|
6
|
+
failures json[] NOT NULL,
|
|
7
|
+
wait_for_command_ids uuid[] DEFAULT '{}'::uuid[],
|
|
8
|
+
|
|
9
|
+
PRIMARY KEY (id)
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
CREATE INDEX index_kommando_scheduled_commands_on_handle_at ON kommando_scheduled_commands USING btree (handle_at);
|
data/lib/kommando.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative './kommando/command'
|
|
2
|
+
require_relative './kommando/command_result'
|
|
3
|
+
require_relative './kommando/scheduled_command_runner'
|
|
4
|
+
require_relative './kommando/scheduled_command_worker'
|
|
5
|
+
require_relative './kommando/command_plugins/base'
|
|
6
|
+
require_relative './kommando/command_plugins/execute'
|
|
7
|
+
require_relative './kommando/command_plugins/schedule'
|
|
8
|
+
require_relative './kommando/command_plugins/validate'
|
|
9
|
+
require_relative './kommando/command_plugins/auto_schedule'
|
|
10
|
+
|
|
11
|
+
module Kommando
|
|
12
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative './command_plugins/base'
|
|
2
|
+
require_relative './command_plugins/execute'
|
|
3
|
+
require_relative './command_plugins/schedule'
|
|
4
|
+
|
|
5
|
+
module Kommando
|
|
6
|
+
class Command
|
|
7
|
+
class MissingDependencyError < StandardError; end
|
|
8
|
+
|
|
9
|
+
class MissingParameterError < StandardError; end
|
|
10
|
+
|
|
11
|
+
class ReservedParameterError < StandardError; end
|
|
12
|
+
|
|
13
|
+
class UnknownCommandError < StandardError; end
|
|
14
|
+
|
|
15
|
+
@options = {}
|
|
16
|
+
|
|
17
|
+
def self.plugin(plugin, *args)
|
|
18
|
+
include plugin::InstanceMethods if defined?(plugin::InstanceMethods)
|
|
19
|
+
extend plugin::ClassMethods if defined?(plugin::ClassMethods)
|
|
20
|
+
plugin.configure(self, *args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
plugin CommandPlugins::Base
|
|
24
|
+
plugin CommandPlugins::Execute
|
|
25
|
+
plugin CommandPlugins::Schedule
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Kommando
|
|
2
|
+
module CommandPlugins
|
|
3
|
+
module AutoSchedule
|
|
4
|
+
def self.configure(command_klass, options)
|
|
5
|
+
command_klass.options[:default_handle_at] = options.fetch(:handle_at)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def schedule(dependencies, parameters)
|
|
10
|
+
super(dependencies, { handle_at: options[:default_handle_at].call }.merge(parameters))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Kommando
|
|
2
|
+
module CommandPlugins
|
|
3
|
+
module Base
|
|
4
|
+
def self.configure(command_klass, *args)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
attr_reader :options
|
|
9
|
+
|
|
10
|
+
def inherited(subclass)
|
|
11
|
+
super
|
|
12
|
+
subclass.instance_variable_set(:@options, options.dup)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module Kommando
|
|
4
|
+
module CommandPlugins
|
|
5
|
+
module Execute
|
|
6
|
+
def self.configure(command_klass, *args)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def execute(dependencies, parameters)
|
|
11
|
+
context = {
|
|
12
|
+
dependencies: dependencies,
|
|
13
|
+
parameters: parameters,
|
|
14
|
+
instance: new,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
context = _before_execute(context)
|
|
18
|
+
return _halt_with_failure(context) if context.key?(:halt)
|
|
19
|
+
|
|
20
|
+
context = _execute(context)
|
|
21
|
+
return _halt_with_failure(context) if context.key?(:halt)
|
|
22
|
+
|
|
23
|
+
context = _after_execute(context)
|
|
24
|
+
return _halt_with_failure(context) if context.key?(:halt)
|
|
25
|
+
|
|
26
|
+
_execute_context_to_result(context)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def _before_execute(context)
|
|
30
|
+
unless context[:parameters].key?(:command_id)
|
|
31
|
+
context[:parameters] = context[:parameters].merge({ command_id: SecureRandom.uuid })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def _execute(context)
|
|
38
|
+
context.merge(execute_return_value: context[:instance].handle(context[:dependencies], context[:parameters]))
|
|
39
|
+
rescue StandardError => error
|
|
40
|
+
context.merge(halt: { error: :unhandled_exception, data: error })
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def _after_execute(context)
|
|
44
|
+
case context[:execute_return_value]
|
|
45
|
+
when CommandResult::Failure
|
|
46
|
+
context.merge(halt: context[:execute_return_value])
|
|
47
|
+
else
|
|
48
|
+
context
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def _execute_context_to_result(context)
|
|
53
|
+
CommandResult.success(_execute_context_to_data({}, context))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def _halt_with_failure(context)
|
|
57
|
+
case context[:halt]
|
|
58
|
+
when CommandResult::Failure
|
|
59
|
+
context[:halt]
|
|
60
|
+
else
|
|
61
|
+
CommandResult.failure(_execute_context_to_data({}, context).merge({ error: context[:halt][:error], details: context[:halt][:data] }))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def _execute_context_to_data(data, context)
|
|
66
|
+
data.merge(context.slice(:parameters)).merge(command: name)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
def reserved_parameter_keys
|
|
71
|
+
[:command_id]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
module InstanceMethods
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module Kommando
|
|
4
|
+
module CommandPlugins
|
|
5
|
+
module Schedule
|
|
6
|
+
def self.configure(command_klass, *args)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def schedule(dependencies, parameters)
|
|
11
|
+
unless dependencies.key?(:schedule_adapter)
|
|
12
|
+
raise Command::MissingDependencyError, 'You need to provide the `:schedule_adapter` dependency'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
unless parameters.key?(:handle_at)
|
|
16
|
+
raise Command::MissingParameterError, 'You need to provide the `:handle_at` parameter'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context = {
|
|
20
|
+
parameters: parameters,
|
|
21
|
+
schedule_adapter: dependencies[:schedule_adapter],
|
|
22
|
+
handle_at: parameters[:handle_at].getutc,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
context = _before_schedule(context)
|
|
26
|
+
return _halt_schedule_with_failure(context) if context.key?(:halt)
|
|
27
|
+
|
|
28
|
+
context = _schedule(context)
|
|
29
|
+
return _halt_schedule_with_failure(context) if context.key?(:halt)
|
|
30
|
+
|
|
31
|
+
context = _after_schedule(context)
|
|
32
|
+
return _halt_schedule_with_failure(context) if context.key?(:halt)
|
|
33
|
+
|
|
34
|
+
_schedule_context_to_result(context)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def _before_schedule(context)
|
|
38
|
+
unless context[:parameters].key?(:command_id)
|
|
39
|
+
context[:parameters] = context[:parameters].merge({ command_id: SecureRandom.uuid })
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def _schedule(context)
|
|
46
|
+
context.merge(schedule_return_value: context[:schedule_adapter].schedule!(name, context[:parameters], context[:handle_at]))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def _after_schedule(context)
|
|
50
|
+
context
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def _before_execute(context)
|
|
54
|
+
context[:instance].scheduled_command_results = []
|
|
55
|
+
super(context)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def _after_execute(context)
|
|
59
|
+
results = context[:instance].scheduled_command_results
|
|
60
|
+
|
|
61
|
+
if failed_result = results.find(&:error?)
|
|
62
|
+
super(context.merge(halt: CommandResult.failure({ command: name, parameters: context[:parameters] }.merge({ error: :scheduling_error, details: failed_result.error }))))
|
|
63
|
+
else
|
|
64
|
+
super(context)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def _schedule_context_to_result(context)
|
|
69
|
+
CommandResult.success(_schedule_context_to_data({}, context))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def _execute_context_to_data(data, context)
|
|
73
|
+
results = context[:instance].scheduled_command_results
|
|
74
|
+
super(data.merge(scheduled_commands: results.map(&:value)), context)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def _schedule_context_to_data(data, context)
|
|
78
|
+
data.merge(context.slice(:parameters)).merge(command: name)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def _halt_schedule_with_failure(context)
|
|
82
|
+
case context[:halt]
|
|
83
|
+
when CommandResult::Failure
|
|
84
|
+
context[:halt]
|
|
85
|
+
else
|
|
86
|
+
CommandResult.failure(_schedule_context_to_data({}, context).merge({ error: context[:halt][:error], details: context[:halt][:data] }))
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
def reserved_parameter_keys
|
|
92
|
+
super.concat([:handle_at, :wait_for_command_ids, :command_id]).uniq
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
module InstanceMethods
|
|
97
|
+
attr_accessor :scheduled_command_results
|
|
98
|
+
|
|
99
|
+
def scheduled(result)
|
|
100
|
+
@scheduled_command_results.push(result)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Kommando
|
|
2
|
+
module CommandPlugins
|
|
3
|
+
module Validate
|
|
4
|
+
def self.configure(command_klass, *args)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
def _before_execute(context)
|
|
9
|
+
if const_defined?('Schema')
|
|
10
|
+
schema_keys = const_get('Schema').schema.key_map.map(&:name).map(&:to_sym)
|
|
11
|
+
|
|
12
|
+
if forbidden_key = reserved_parameter_keys.find { |key| schema_keys.include?(key) }
|
|
13
|
+
raise Command::ReservedParameterError, "The `#{forbidden_key}` parameter is reserved and cannot be used in command schema"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
reserved_parameters = context[:parameters].slice(*reserved_parameter_keys)
|
|
17
|
+
validation_result = const_get('Schema').new.call(context[:parameters])
|
|
18
|
+
|
|
19
|
+
if validation_result.success?
|
|
20
|
+
super(context.merge(parameters: validation_result.to_h.merge(reserved_parameters)))
|
|
21
|
+
else
|
|
22
|
+
super(context.merge(halt: { error: :schema_error, data: validation_result.errors.to_h }))
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
super(context)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def _before_schedule(context)
|
|
30
|
+
if const_defined?('Schema')
|
|
31
|
+
schema_keys = const_get('Schema').schema.key_map.map(&:name).map(&:to_sym)
|
|
32
|
+
|
|
33
|
+
if forbidden_key = reserved_parameter_keys.find { |key| schema_keys.include?(key) }
|
|
34
|
+
raise Command::ReservedParameterError, "The `#{forbidden_key}` parameter is reserved and cannot be used in command schema"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
reserved_parameters = context[:parameters].slice(*reserved_parameter_keys)
|
|
38
|
+
validation_result = const_get('Schema').new.call(context[:parameters])
|
|
39
|
+
|
|
40
|
+
if validation_result.success?
|
|
41
|
+
super(context.merge(parameters: validation_result.to_h.merge(reserved_parameters)))
|
|
42
|
+
else
|
|
43
|
+
super(context.merge(halt: { error: :schema_error, data: validation_result.errors.to_h }))
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
super(context)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Kommando
|
|
2
|
+
class CommandResult
|
|
3
|
+
class NonExistentError < StandardError; end
|
|
4
|
+
|
|
5
|
+
class NonExistentValue < StandardError; end
|
|
6
|
+
|
|
7
|
+
def self.success(value)
|
|
8
|
+
Success.new(value)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.failure(error)
|
|
12
|
+
Failure.new(error)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Success
|
|
16
|
+
attr_reader :value
|
|
17
|
+
|
|
18
|
+
def initialize(value)
|
|
19
|
+
raise ArgumentError, 'missing `:command` key' unless value.key?(:command)
|
|
20
|
+
raise ArgumentError, 'missing `:parameters` key' unless value.key?(:parameters)
|
|
21
|
+
@value = value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def error
|
|
25
|
+
raise NonExistentError, 'Success results do not have errors'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def success?
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def error?
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def deconstruct_keys(_)
|
|
37
|
+
{ value: @value[:command] }.merge(@value)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class Failure
|
|
42
|
+
attr_reader :error
|
|
43
|
+
|
|
44
|
+
def initialize(error)
|
|
45
|
+
raise ArgumentError, 'missing `:error` key' unless error.key?(:error)
|
|
46
|
+
raise ArgumentError, 'missing `:command` key' unless error.key?(:command)
|
|
47
|
+
raise ArgumentError, 'missing `:parameters` key' unless error.key?(:parameters)
|
|
48
|
+
@error = error
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def value
|
|
52
|
+
raise NonExistentValue, 'Failure results do not have values'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def success?
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def error?
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def deconstruct_keys(_)
|
|
64
|
+
{ error: error }.merge(@error)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
|
|
3
|
+
module Kommando
|
|
4
|
+
module ScheduledCommandAdapters
|
|
5
|
+
class ActiveRecord < ::ActiveRecord::Base
|
|
6
|
+
self.table_name = 'kommando_scheduled_commands'
|
|
7
|
+
|
|
8
|
+
def self.schedule!(command, parameters, handle_at)
|
|
9
|
+
create!({
|
|
10
|
+
id: parameters.fetch(:command_id),
|
|
11
|
+
name: command,
|
|
12
|
+
parameters: parameters,
|
|
13
|
+
handle_at: handle_at,
|
|
14
|
+
failures: [],
|
|
15
|
+
wait_for_command_ids: parameters.fetch(:wait_for_command_ids, []),
|
|
16
|
+
})
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.fetch!(&block)
|
|
20
|
+
transaction do
|
|
21
|
+
record = lock('FOR UPDATE SKIP LOCKED').
|
|
22
|
+
where('TIMEZONE(\'UTC\', NOW()) >= handle_at').
|
|
23
|
+
where.not("wait_for_command_ids && (SELECT array_agg(id) FROM #{table_name})").
|
|
24
|
+
order(handle_at: :asc).
|
|
25
|
+
limit(1).
|
|
26
|
+
first
|
|
27
|
+
|
|
28
|
+
if record
|
|
29
|
+
result = block.call(record.name, record.parameters)
|
|
30
|
+
|
|
31
|
+
if result.success?
|
|
32
|
+
record.destroy
|
|
33
|
+
else
|
|
34
|
+
record.update!({
|
|
35
|
+
failures: record.failures.append(result.error),
|
|
36
|
+
handle_at: (record.handle_at + 5.minutes).getutc,
|
|
37
|
+
})
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parameters
|
|
44
|
+
@parameters ||= super.deep_symbolize_keys!
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def failures
|
|
48
|
+
@failures ||= super.map(&:deep_symbolize_keys!)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Kommando
|
|
2
|
+
class ScheduledCommandRunner
|
|
3
|
+
def initialize(coordinator, adapter, dependencies)
|
|
4
|
+
@coordinator = coordinator
|
|
5
|
+
@adapter = adapter
|
|
6
|
+
@dependencies = dependencies
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
fetched_command = false
|
|
11
|
+
|
|
12
|
+
@adapter.fetch! do |command_name, parameters|
|
|
13
|
+
unless self.class.const_defined?(command_name)
|
|
14
|
+
raise Command::UnknownCommandError, "Unknown command `#{command_name}`"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
fetched_command = true
|
|
18
|
+
|
|
19
|
+
self.class.const_get(command_name).execute(@dependencies, parameters)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@coordinator.schedule_next_run(fetched_command ? 0 : 5)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Kommando
|
|
2
|
+
class ScheduledCommandWorker
|
|
3
|
+
def initialize(adapter, dependencies, number_of_threads)
|
|
4
|
+
@adapter = adapter
|
|
5
|
+
@dependencies = dependencies
|
|
6
|
+
@number_of_threads = number_of_threads
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def start
|
|
10
|
+
@pool = Concurrent::FixedThreadPool.new(@number_of_threads)
|
|
11
|
+
@number_of_threads.times { schedule_next_run(0) }
|
|
12
|
+
@pool.wait_for_termination
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def stop
|
|
16
|
+
@pool.shutdown
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def schedule_next_run(delay)
|
|
20
|
+
Concurrent::ScheduledTask.execute(delay, { executor: @pool }, &ScheduledCommandRunner.new(self, @adapter, @dependencies).method(:call))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: edgycircle_kommando
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Strauß
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 1970-01-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: pg
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: dry-validation
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rails
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '6.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '6.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubocop
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '12.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '12.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: minitest
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '5.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '5.0'
|
|
97
|
+
description: Command architecture building blocks.
|
|
98
|
+
email:
|
|
99
|
+
- david.strauss@edgycircle.com
|
|
100
|
+
executables: []
|
|
101
|
+
extensions: []
|
|
102
|
+
extra_rdoc_files: []
|
|
103
|
+
files:
|
|
104
|
+
- db/migrations/1.down.sql
|
|
105
|
+
- db/migrations/1.up.sql
|
|
106
|
+
- lib/kommando.rb
|
|
107
|
+
- lib/kommando/active_record.rb
|
|
108
|
+
- lib/kommando/command.rb
|
|
109
|
+
- lib/kommando/command_plugins/auto_schedule.rb
|
|
110
|
+
- lib/kommando/command_plugins/base.rb
|
|
111
|
+
- lib/kommando/command_plugins/execute.rb
|
|
112
|
+
- lib/kommando/command_plugins/schedule.rb
|
|
113
|
+
- lib/kommando/command_plugins/validate.rb
|
|
114
|
+
- lib/kommando/command_result.rb
|
|
115
|
+
- lib/kommando/scheduled_command_adapters/active_record.rb
|
|
116
|
+
- lib/kommando/scheduled_command_runner.rb
|
|
117
|
+
- lib/kommando/scheduled_command_worker.rb
|
|
118
|
+
- lib/kommando/version.rb
|
|
119
|
+
homepage: https://github.com/edgycircle/kommando
|
|
120
|
+
licenses:
|
|
121
|
+
- Nonstandard
|
|
122
|
+
metadata: {}
|
|
123
|
+
post_install_message:
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
require_paths:
|
|
126
|
+
- lib
|
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: 2.7.0
|
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - ">="
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '0'
|
|
137
|
+
requirements: []
|
|
138
|
+
rubygems_version: 3.1.2
|
|
139
|
+
signing_key:
|
|
140
|
+
specification_version: 4
|
|
141
|
+
summary: ''
|
|
142
|
+
test_files: []
|