|
|
@ -4,24 +4,57 @@ require 'dotenv' |
|
|
|
module HotConstants |
|
|
|
HOTENV = {} # rubocop:disable Style/MutableConstant |
|
|
|
LISTENERS = {} # rubocop:disable Style/MutableConstant |
|
|
|
LOGGER = SemanticLogger[to_s] |
|
|
|
LOGGER = SemanticLogger[self] |
|
|
|
|
|
|
|
class << self |
|
|
|
def initialize |
|
|
|
env_sample = Dotenv.parse('.env.sample') # TODO: able to detect add/remove, then either call define_method or undef_method! |
|
|
|
env_sample.each do |key, value| |
|
|
|
dkey = key.downcase |
|
|
|
method = method_from_value(value) |
|
|
|
singleton_class.define_method(dkey) { method.call(key, value) } |
|
|
|
LOGGER.info("create method <#{dkey}> performing <#{method.name}> with default value <#{value}> ") |
|
|
|
@old_definitions = {} |
|
|
|
@hot_definitions = {} |
|
|
|
|
|
|
|
set_definitions |
|
|
|
load_values |
|
|
|
end |
|
|
|
|
|
|
|
def set_definitions |
|
|
|
LOGGER.info('--set_definitions') |
|
|
|
|
|
|
|
new_definitions = Dotenv.parse('.env.sample') |
|
|
|
|
|
|
|
definitions_to_actualize = {} |
|
|
|
@old_definitions.merge(new_definitions) do |key, old_value, new_value| |
|
|
|
definitions_to_actualize[key] = new_value if new_value != old_value |
|
|
|
end |
|
|
|
definitions_to_actualize.each { |key, value| define_method(:actualize, key, value) } |
|
|
|
|
|
|
|
def reload! |
|
|
|
HOTENV.replace Dotenv.parse # TODO: detect true changes before processing block below (or yield) |
|
|
|
LISTENERS.each_pair do |key, block| |
|
|
|
perform_change(key, block) |
|
|
|
@old_definitions = new_definitions |
|
|
|
|
|
|
|
definitions_to_remove = @hot_definitions.except(*new_definitions.keys) |
|
|
|
definitions_to_remove.each_pair do |key, dkey| |
|
|
|
LOGGER.info("remove method <#{dkey}>") |
|
|
|
singleton_class.undef_method(dkey) |
|
|
|
@hot_definitions.delete(key) |
|
|
|
end |
|
|
|
|
|
|
|
definitions_to_add = new_definitions.except(*@hot_definitions.keys) |
|
|
|
definitions_to_add.each { |key, value| define_method(:add, key, value) } |
|
|
|
end |
|
|
|
|
|
|
|
def load_values |
|
|
|
LOGGER.info('--load_values') |
|
|
|
|
|
|
|
new_env = Dotenv.parse |
|
|
|
constants_to_delete = HOTENV.except(*new_env.keys) |
|
|
|
constants_to_delete.each do |constant| |
|
|
|
LOGGER.info("constant <#{constant}> reverts to default value <#{}>") |
|
|
|
end |
|
|
|
|
|
|
|
constants_to_add = new_env.except(*HOTENV.keys) |
|
|
|
constants_to_add.each do |constant| |
|
|
|
LOGGER.info("constant to add <#{constant}>") |
|
|
|
end |
|
|
|
|
|
|
|
HOTENV.replace new_env |
|
|
|
# LISTENERS.each_pair { |k, b| perform_change(k, b) } |
|
|
|
end |
|
|
|
|
|
|
|
def on_change(key, &block) |
|
|
@ -31,6 +64,17 @@ module HotConstants |
|
|
|
|
|
|
|
private |
|
|
|
|
|
|
|
def define_method(mode, key, value) |
|
|
|
dkey = key.downcase |
|
|
|
method = infer_method_from_value(value) |
|
|
|
|
|
|
|
inferred_type = method.name.to_s.split('_')[1] |
|
|
|
LOGGER.info("#{mode} method <#{dkey}> of type <#{inferred_type}> with default value <#{value}> ") |
|
|
|
|
|
|
|
singleton_class.define_method(dkey) { method.call(key, value) } |
|
|
|
@hot_definitions.store(key, dkey) |
|
|
|
end |
|
|
|
|
|
|
|
def perform_change(key, block) |
|
|
|
old_value = nil # TODO: remember last previous value |
|
|
|
new_value = method(key).call |
|
|
@ -41,7 +85,7 @@ module HotConstants |
|
|
|
def load_integer(key, default) = HOTENV.fetch(key, default).to_i |
|
|
|
def load_string(key, default) = HOTENV.fetch(key, default) |
|
|
|
|
|
|
|
def method_from_value(value) |
|
|
|
def infer_method_from_value(value) |
|
|
|
case value.downcase |
|
|
|
when /^(true|false)$/ then method(:load_boolean) |
|
|
|
when /^\d+$/ then method(:load_integer) |
|
|
@ -50,6 +94,5 @@ module HotConstants |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
# initialize on first require |
|
|
|
initialize |
|
|
|
initialize # done once on first require, cause this is just a module (not a class!) |
|
|
|
end |