| 
					
					
						
							
						
					
					
				 | 
				@ -4,24 +4,57 @@ require 'dotenv' | 
			
		
		
	
		
			
				 | 
				 | 
				module HotConstants | 
				 | 
				 | 
				module HotConstants | 
			
		
		
	
		
			
				 | 
				 | 
				  HOTENV = {} # rubocop:disable Style/MutableConstant | 
				 | 
				 | 
				  HOTENV = {} # rubocop:disable Style/MutableConstant | 
			
		
		
	
		
			
				 | 
				 | 
				  LISTENERS = {} # rubocop:disable Style/MutableConstant | 
				 | 
				 | 
				  LISTENERS = {} # rubocop:disable Style/MutableConstant | 
			
		
		
	
		
			
				 | 
				 | 
				  LOGGER = SemanticLogger[to_s] | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  LOGGER = SemanticLogger[self] | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				  class << self | 
				 | 
				 | 
				  class << self | 
			
		
		
	
		
			
				 | 
				 | 
				    def initialize | 
				 | 
				 | 
				    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 | 
				 | 
				 | 
				    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 | 
				 | 
				 | 
				      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 | 
				 | 
				 | 
				      end | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      HOTENV.replace new_env | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				      # LISTENERS.each_pair { |k, b| perform_change(k, b) } | 
			
		
		
	
		
			
				 | 
				 | 
				    end | 
				 | 
				 | 
				    end | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				    def on_change(key, &block) | 
				 | 
				 | 
				    def on_change(key, &block) | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -31,6 +64,17 @@ module HotConstants | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				    private | 
				 | 
				 | 
				    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) | 
				 | 
				 | 
				    def perform_change(key, block) | 
			
		
		
	
		
			
				 | 
				 | 
				      old_value = nil # TODO: remember last previous value | 
				 | 
				 | 
				      old_value = nil # TODO: remember last previous value | 
			
		
		
	
		
			
				 | 
				 | 
				      new_value = method(key).call | 
				 | 
				 | 
				      new_value = method(key).call | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -41,7 +85,7 @@ module HotConstants | 
			
		
		
	
		
			
				 | 
				 | 
				    def load_integer(key, default) = HOTENV.fetch(key, default).to_i | 
				 | 
				 | 
				    def load_integer(key, default) = HOTENV.fetch(key, default).to_i | 
			
		
		
	
		
			
				 | 
				 | 
				    def load_string(key, default) = HOTENV.fetch(key, default) | 
				 | 
				 | 
				    def load_string(key, default) = HOTENV.fetch(key, default) | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				    def method_from_value(value) | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				    def infer_method_from_value(value) | 
			
		
		
	
		
			
				 | 
				 | 
				      case value.downcase | 
				 | 
				 | 
				      case value.downcase | 
			
		
		
	
		
			
				 | 
				 | 
				      when /^(true|false)$/ then method(:load_boolean) | 
				 | 
				 | 
				      when /^(true|false)$/ then method(:load_boolean) | 
			
		
		
	
		
			
				 | 
				 | 
				      when /^\d+$/ then method(:load_integer) | 
				 | 
				 | 
				      when /^\d+$/ then method(:load_integer) | 
			
		
		
	
	
		
			
				| 
					
					
					
						
							
						
					
				 | 
				@ -50,6 +94,5 @@ module HotConstants | 
			
		
		
	
		
			
				 | 
				 | 
				    end | 
				 | 
				 | 
				    end | 
			
		
		
	
		
			
				 | 
				 | 
				  end | 
				 | 
				 | 
				  end | 
			
		
		
	
		
			
				 | 
				 | 
				
 | 
				 | 
				 | 
				
 | 
			
		
		
	
		
			
				 | 
				 | 
				  # initialize on first require | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				  initialize | 
				 | 
				 | 
				 | 
			
		
		
	
		
			
				 | 
				 | 
				 | 
				 | 
				 | 
				  initialize # done once on first require, cause this is just a module (not a class!) | 
			
		
		
	
		
			
				 | 
				 | 
				end | 
				 | 
				 | 
				end |