You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
3.3 KiB

5 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
5 months ago
3 months ago
5 months ago
3 months ago
5 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
5 months ago
  1. require 'dotenv'
  2. module Hot
  3. # Hot Live Constants
  4. module Constants
  5. include SemanticLogger::Loggable
  6. HOTENV = {} # rubocop:disable Style/MutableConstant
  7. LISTENERS = {} # rubocop:disable Style/MutableConstant
  8. class << self
  9. def initialize
  10. @old_definitions = {}
  11. @hot_definitions = {}
  12. logger.level = :fatal
  13. load_definitions
  14. load_values
  15. logger.level = :info
  16. end
  17. def load_definitions
  18. logger.debug('--load_definitions3')
  19. new_definitions = Dotenv.parse('.env.sample')
  20. definitions_to_actualize = {}
  21. @old_definitions.merge(new_definitions) do |key, old_value, new_value|
  22. definitions_to_actualize[key] = new_value if new_value != old_value
  23. end
  24. definitions_to_actualize.each { |key, value| define_method(:actualize, key, value) }
  25. @old_definitions = new_definitions
  26. definitions_to_remove = @hot_definitions.except(*new_definitions.keys)
  27. definitions_to_remove.each_pair do |key, dkey|
  28. logger.info("remove method <#{dkey}>")
  29. singleton_class.undef_method(dkey)
  30. @hot_definitions.delete(key)
  31. end
  32. definitions_to_add = new_definitions.except(*@hot_definitions.keys)
  33. definitions_to_add.each { |key, value| define_method(:add, key, value) }
  34. end
  35. def load_values
  36. logger.debug('--load_values')
  37. new_env = Dotenv.parse
  38. constants_to_delete = HOTENV.except(*new_env.keys)
  39. constants_to_delete.each do |name, _|
  40. # FIXME: default should read default and type from @old_definitions
  41. type, default = @old_definitions[name]
  42. logger.info("constant <#{name}> reverts to default value <#{default}> of type <#{type}>")
  43. end
  44. constants_to_add = new_env.except(*HOTENV.keys)
  45. constants_to_add.each do |constant|
  46. logger.info("constant to add <#{constant}>")
  47. end
  48. HOTENV.replace new_env
  49. LISTENERS.each_pair { |k, b| perform_change(k, b) }
  50. end
  51. def on_change(key, &block)
  52. LISTENERS[key.downcase.to_sym] = block
  53. perform_change(key, block)
  54. end
  55. private
  56. def define_method(mode, key, value)
  57. dkey = key.downcase
  58. method = infer_method_from_value(value)
  59. inferred_type = method.name.to_s.split('_')[1]
  60. logger.info("#{mode} method <#{dkey}> of type <#{inferred_type}> with default value <#{value}> ")
  61. singleton_class.define_method(dkey) { method.call(key, value) }
  62. @hot_definitions.store(key, dkey)
  63. end
  64. def perform_change(key, block)
  65. old_value = nil # TODO: remember last previous value
  66. new_value = method(key).call
  67. block.call(new_value, old_value)
  68. end
  69. def load_boolean(key, default) = HOTENV.fetch(key, default).to_s.downcase == 'true'
  70. def load_integer(key, default) = HOTENV.fetch(key, default).to_i
  71. def load_string(key, default) = HOTENV.fetch(key, default)
  72. def infer_method_from_value(value)
  73. case value.downcase
  74. when /^(true|false)$/ then method(:load_boolean)
  75. when /^\d+$/ then method(:load_integer)
  76. else method(:load_string)
  77. end
  78. end
  79. end
  80. initialize # done once on first require, cause this is just a module (not a class!)
  81. end
  82. end