Class: Kaal::Config::FileLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/kaal/config/file_loader.rb,
sig/kaal/config/file_loader.rbs

Overview

Loads Kaal runtime configuration from config/kaal.yml and KAAL_* env vars.

Defined Under Namespace

Classes: EnvValue

Constant Summary collapse

ENV_KEY_MAP =

Returns:

  • (::Hash[::String, ::Symbol])
{
  'KAAL_BACKEND' => :backend,
  'KAAL_NAMESPACE' => :namespace,
  'KAAL_TICK_INTERVAL' => :tick_interval,
  'KAAL_WINDOW_LOOKBACK' => :window_lookback,
  'KAAL_WINDOW_LOOKAHEAD' => :window_lookahead,
  'KAAL_LEASE_TTL' => :lease_ttl,
  'KAAL_SCHEDULER_CONFIG_PATH' => :scheduler_config_path,
  'KAAL_ENABLE_LOG_DISPATCH_REGISTRY' => :enable_log_dispatch_registry,
  'KAAL_ENABLE_DISPATCH_RECOVERY' => :enable_dispatch_recovery,
  'KAAL_RECOVERY_WINDOW' => :recovery_window,
  'KAAL_RECOVERY_STARTUP_JITTER' => :recovery_startup_jitter,
  'KAAL_TIME_ZONE' => :time_zone,
  'KAAL_SCHEDULER_CONFLICT_POLICY' => :scheduler_conflict_policy,
  'KAAL_SCHEDULER_MISSING_FILE_POLICY' => :scheduler_missing_file_policy,
  'KAAL_DELAYED_JOB_ALLOWED_CLASS_PREFIXES' => :delayed_job_allowed_class_prefixes
}.freeze
CONFIG_KEY_TO_ENV_KEY =
ENV_KEY_MAP.invert.freeze
CONFIGURATION_ASSIGNERS =
{
  namespace: ->(config, value) { config.namespace = value },
  tick_interval: ->(config, value) { config.tick_interval = value },
  window_lookback: ->(config, value) { config.window_lookback = value },
  window_lookahead: ->(config, value) { config.window_lookahead = value },
  lease_ttl: ->(config, value) { config.lease_ttl = value },
  scheduler_config_path: ->(config, value) { config.scheduler_config_path = value },
  enable_log_dispatch_registry: ->(config, value) { config.enable_log_dispatch_registry = value },
  enable_dispatch_recovery: ->(config, value) { config.enable_dispatch_recovery = value },
  recovery_window: ->(config, value) { config.recovery_window = value },
  recovery_startup_jitter: ->(config, value) { config.recovery_startup_jitter = value },
  time_zone: ->(config, value) { config.time_zone = value },
  scheduler_conflict_policy: ->(config, value) { config.scheduler_conflict_policy = value },
  scheduler_missing_file_policy: ->(config, value) { config.scheduler_missing_file_policy = value },
  delayed_job_allowed_class_prefixes: ->(config, value) { config.delayed_job_allowed_class_prefixes = value },
  logger: ->(config, value) { config.logger = value }
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(configuration:, runtime_context:, env: ENV) ⇒ FileLoader

Returns a new instance of FileLoader.

Parameters:

  • configuration: (Kaal::rbs_any)
  • runtime_context: (Kaal::rbs_any)
  • env: (Kaal::rbs_any) (defaults to: ENV)


88
89
90
91
92
93
# File 'lib/kaal/config/file_loader.rb', line 88

def initialize(configuration:, runtime_context:, env: ENV)
  @configuration = configuration
  @runtime_context = runtime_context
  @env = env
  @config_key_to_env_key = CONFIG_KEY_TO_ENV_KEY
end

Instance Method Details

#apply_configuration(config) ⇒ Kaal::rbs_any

Parameters:

  • config (Kaal::rbs_any)

Returns:

  • (Kaal::rbs_any)


168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/kaal/config/file_loader.rb', line 168

def apply_configuration(config)
  normalized = Kaal::Support::HashTools.symbolize_keys(config)
  backend_config = normalized.delete(:backend_config) || {}
  backend_name = normalized.delete(:backend)
  @configuration.apply_backend_runtime_context(@runtime_context)

  normalized.each do |key, value|
    apply_configuration_value(key, value)
  end

  @configuration.backend_config = backend_config
  @configuration.backend = backend_name if backend_name
end

#apply_env_overrides(config) ⇒ Kaal::rbs_any

Parameters:

  • config (Kaal::rbs_any)

Returns:

  • (Kaal::rbs_any)


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/kaal/config/file_loader.rb', line 141

def apply_env_overrides(config)
  merged = Kaal::Support::HashTools.deep_dup(config)

  ENV_KEY_MAP.each do |env_key, config_key|
    next unless @env.key?(env_key)

    merged[config_key.to_s] = coerce_env_value(config_key, @env.fetch(env_key))
  end

  backend_url = @env['KAAL_BACKEND_URL']&.to_s&.strip
  if @env.key?('KAAL_BACKEND_URL')
    backend_config = hash_section(merged['backend_config'])
    if backend_config.key?('connection')
      backend_config['connection'] = backend_url
    else
      backend_config['url'] = backend_url
    end
    merged['backend_config'] = backend_config
  end

  merged
end

#coerce_boolean_env ⇒ true, ...

Parameters:

  • key (Kaal::rbs_any)
  • value (Kaal::rbs_any)

Returns:

  • (true, false, Kaal::rbs_any)


28
# File 'sig/kaal/config/file_loader.rbs', line 28

def coerce_boolean_env: (Kaal::rbs_any key, Kaal::rbs_any value) -> (true | false | Kaal::rbs_any)

#coerce_env_value(key, value) ⇒ Kaal::rbs_any

Parameters:

  • key (Kaal::rbs_any)
  • value (Kaal::rbs_any)

Returns:

  • (Kaal::rbs_any)


164
165
166
# File 'lib/kaal/config/file_loader.rb', line 164

def coerce_env_value(key, value)
  EnvValue.new(value).coerce(key:, env_name: @config_key_to_env_key.fetch(key))
end

#env_name_for ⇒ Kaal::rbs_any

Parameters:

  • key (Kaal::rbs_any)

Returns:

  • (Kaal::rbs_any)


30
# File 'sig/kaal/config/file_loader.rbs', line 30

def env_name_for: (Kaal::rbs_any key) -> Kaal::rbs_any

#hash_section(value) ⇒ ::Hash[Kaal::rbs_any, Kaal::rbs_any], Kaal::rbs_any

Parameters:

  • value (Kaal::rbs_any)

Returns:

  • (::Hash[Kaal::rbs_any, Kaal::rbs_any], Kaal::rbs_any)


130
131
132
133
134
135
136
137
138
139
# File 'lib/kaal/config/file_loader.rb', line 130

def hash_section(value)
  case value
  in Hash
    Kaal::Support::HashTools.stringify_keys(Kaal::Support::HashTools.deep_dup(value))
  in nil
    {}
  else
    raise Kaal::ConfigurationError, 'Kaal config sections must be mappings'
  end
end

#load(path: 'config/kaal.yml') ⇒ Kaal::rbs_any

Parameters:

  • path: (::String) (defaults to: 'config/kaal.yml')

Returns:

  • (Kaal::rbs_any)


95
96
97
98
99
100
101
102
103
# File 'lib/kaal/config/file_loader.rb', line 95

def load(path: 'config/kaal.yml')
  absolute_path = @runtime_context.resolve_path(path)
  payload = File.exist?(absolute_path) ? parse_yaml(absolute_path) : {}
  merged = merge_environment_config(payload)
  merged = apply_env_overrides(merged)
  apply_configuration(merged)
  @configuration.validate!
  @configuration
end

#merge_environment_config(payload) ⇒ Kaal::rbs_any

Parameters:

  • payload (Kaal::rbs_any)

Returns:

  • (Kaal::rbs_any)


123
124
125
126
127
128
# File 'lib/kaal/config/file_loader.rb', line 123

def merge_environment_config(payload)
  defaults = hash_section(payload['defaults'])
  environment = hash_section(payload[@runtime_context.environment_name])

  Kaal::Support::HashTools.deep_merge(defaults, environment)
end

#parse_yaml(path) ⇒ Kaal::rbs_any

Parameters:

  • path (Kaal::rbs_any)

Returns:

  • (Kaal::rbs_any)


107
108
109
110
111
112
113
114
115
# File 'lib/kaal/config/file_loader.rb', line 107

def parse_yaml(path)
  rendered = render_yaml(path)
  parsed = YAML.safe_load(rendered, aliases: true) || {}
  raise Kaal::ConfigurationError, "Expected Kaal config YAML root to be a mapping in #{path}" unless parsed.is_a?(Hash)

  Kaal::Support::HashTools.stringify_keys(parsed)
rescue Psych::Exception => e
  raise Kaal::ConfigurationError, "Failed to parse Kaal config YAML at #{path}: #{e.message}"
end