-
# frozen_string_literal: true
-
-
38
require "logger"
-
38
require "forwardable"
-
-
38
module Tobox
-
38
class Configuration
-
38
extend Forwardable
-
-
38
attr_reader :plugins, :handlers, :lifecycle_events, :arguments_handler, :default_logger, :database,
-
:fetcher_class, :worker_class,
-
:config
-
-
38
def_delegator :@config, :[]
-
-
7
DEFAULT_CONFIGURATION = {
-
31
environment: ENV.fetch("APP_ENV", "development"),
-
logger: nil,
-
log_level: nil,
-
database_uri: nil,
-
database_options: nil,
-
table: :outbox,
-
visibility_column: :run_at,
-
attempts_column: :attempts,
-
created_at_column: nil,
-
batch_size: 1,
-
max_attempts: 10,
-
exponential_retry_factor: 2,
-
wait_for_events_delay: 5,
-
shutdown_timeout: 10,
-
grace_shutdown_timeout: 5,
-
concurrency: 4, # TODO: CPU count
-
worker: :thread
-
}.freeze
-
-
38
LOG_FORMAT_PATTERN = "%s, [%s #%d (th: %s)] %5s -- %s: %s\n"
-
38
DEFAULT_LOG_FORMATTER = Class.new(Logger::Formatter) do
-
38
def call(severity, time, progname, msg)
-
1651
format(LOG_FORMAT_PATTERN, severity[0, 1], format_datetime(time), Process.pid,
-
Thread.current.name || Thread.current.object_id, severity, progname, msg2str(msg))
-
end
-
end.new
-
-
38
def initialize(name = nil, &block)
-
1183
@name = name
-
1183
@config = DEFAULT_CONFIGURATION.dup
-
-
1183
@lifecycle_events = {}
-
1183
@handlers = {}
-
1183
@message_to_arguments = nil
-
1183
@plugins = []
-
1183
@fetcher_class = Class.new(Fetcher)
-
1183
@worker_class = Class.new(Worker)
-
-
1183
if block
-
1084
case block.arity
-
when 0
-
190
instance_exec(&block)
-
when 1
-
960
yield(self)
-
else
-
33
raise Error, "configuration does not support blocks with more than one variable"
-
end
-
end
-
-
1117
env = @config[:environment]
-
1117
@default_logger = @config[:logger] || Logger.new(STDERR, formatter: DEFAULT_LOG_FORMATTER) # rubocop:disable Style/GlobalStdStream
-
1117
@default_logger.level = @config[:log_level] || (env == "production" ? Logger::INFO : Logger::DEBUG)
-
-
1117
@database = if @config[:database_uri]
-
58
database_opts = @config[:database_options] || {}
-
58
database_opts[:max_connections] ||= (@config[:concurrency] if @config[:worker] == :thread)
-
58
db = Sequel.connect(@config[:database_uri].to_s, database_opts)
-
87
Array(@lifecycle_events[:database_connect]).each { |cb| cb.call(db) }
-
58
db
-
else
-
1059
Sequel::DATABASES.first
-
end
-
1117
raise Error, "no database found" unless @database
-
-
1117
if @database.frozen?
-
1021
raise "#{@database} must have the :date_arithmetic extension loaded" unless Sequel.respond_to?(:date_add)
-
else
-
96
@database.extension :date_arithmetic
-
96
@database.loggers << @default_logger unless @config[:environment] == "production"
-
end
-
-
1117
freeze
-
end
-
-
38
def on(*event_types, &callback)
-
330
callback_events = (@handlers[callback] ||= [])
-
330
event_types.each do |event_type|
-
396
callback_events << event_type.to_sym
-
end
-
330
self
-
end
-
-
38
def on_start(&callback)
-
58
(@lifecycle_events[:on_start] ||= []) << callback
-
58
self
-
end
-
-
38
def on_stop(&callback)
-
33
(@lifecycle_events[:on_stop] ||= []) << callback
-
33
self
-
end
-
-
38
def on_before_event(&callback)
-
81
(@lifecycle_events[:before_event] ||= []) << callback
-
81
self
-
end
-
-
38
def on_after_event(&callback)
-
89
(@lifecycle_events[:after_event] ||= []) << callback
-
89
self
-
end
-
-
38
def on_error_event(&callback)
-
205
(@lifecycle_events[:error_event] ||= []) << callback
-
205
self
-
end
-
-
38
def on_start_worker(&callback)
-
8
(@lifecycle_events[:start_worker] ||= []) << callback
-
8
self
-
end
-
-
38
def on_error_worker(&callback)
-
30
(@lifecycle_events[:error_worker] ||= []) << callback
-
30
self
-
end
-
-
38
def on_database_connect(&callback)
-
29
(@lifecycle_events[:database_connect] ||= []) << callback
-
29
self
-
end
-
-
38
def message_to_arguments(&callback)
-
33
@arguments_handler = callback
-
33
self
-
end
-
-
38
def visibility_type_bool?
-
2157
_, visibility_info = @database.schema(@config[:table]).find do |column, _|
-
17541
column == @config[:visibility_column]
-
end
-
-
2157
raise Error, "a visibility column is required" unless visibility_info
-
-
2157
visibility_info[:type] == :boolean
-
end
-
-
38
def plugin(plugin, **options, &block)
-
238
raise Error, "Cannot add a plugin to a frozen config" if frozen?
-
-
238
plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
-
-
238
return if @plugins.include?(plugin)
-
-
238
@plugins << plugin
-
238
plugin.load_dependencies(self, **options, &block) if plugin.respond_to?(:load_dependencies)
-
-
238
extend(plugin::ConfigurationMethods) if defined?(plugin::ConfigurationMethods)
-
-
238
@fetcher_class.__send__(:include, plugin::FetcherMethods) if defined?(plugin::FetcherMethods)
-
238
@worker_class.__send__(:include, plugin::WorkerMethods) if defined?(plugin::WorkerMethods)
-
-
238
plugin.configure(self, **options, &block) if plugin.respond_to?(:configure)
-
end
-
-
38
def freeze
-
1117
@name.freeze
-
1117
@config.each_value(&:freeze).freeze
-
1117
@handlers.each_value(&:freeze).freeze
-
1117
@lifecycle_events.each_value(&:freeze).freeze
-
1117
@plugins.freeze
-
1117
@database.freeze
-
1117
super
-
end
-
-
38
private
-
-
38
def method_missing(meth, *args, &block)
-
2422
if @config.key?(meth) && args.size == 1
-
2127
@config[meth] = args.first
-
95
elsif /\Aon_(.*)\z/.match(meth) && args.empty?
-
66
on(Regexp.last_match(1).to_sym, &block)
-
else
-
33
super
-
end
-
end
-
-
38
def respond_to_missing?(meth, *args)
-
58
super ||
-
@config.key?(meth) ||
-
/\Aon_(.*)\z/.match(meth)
-
end
-
end
-
end