Browse Source

hot constants definitions

main
pvincent 4 months ago
parent
commit
72dcb76877
  1. 6
      .env.sample
  2. 71
      lib/hot_constants/hot_constants.rb
  3. 4
      lib/monkey_patches/rails_live_reload/watcher.rb

6
.env.sample

@ -13,9 +13,9 @@ LOG_ACTIVE_RECORD=false
LOG_ACTION_VIEW=false LOG_ACTION_VIEW=false
# with default value, type gets inferred from # with default value, type gets inferred from
# INTEGER=1
# STRING=anything
# BOOLEAN=true
INTEGER=1
STRING=456
BOOLEAN=true
# with no default value (evaluated to nil), type must be defined expressively otherwise it reverts to a string type # with no default value (evaluated to nil), type must be defined expressively otherwise it reverts to a string type
# INTEGER=:integer # INTEGER=:integer

71
lib/hot_constants/hot_constants.rb

@ -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

4
lib/monkey_patches/rails_live_reload/watcher.rb

@ -15,8 +15,8 @@ module RailsLiveReload
private private
def before_reload(files) def before_reload(files)
perform_when_change(files, ENV_SAMPLE_FILE) { HotConstants.initialize }
perform_when_change(files, ENV_FILE) { HotConstants.reload! }
perform_when_change(files, ENV_SAMPLE_FILE) { HotConstants.set_definitions }
perform_when_change(files, ENV_FILE) { HotConstants.load_values }
perform_when_change(files, INITIALIZER) { load Rails.root.join('config', 'initializers', 'hot_changes.rb') } perform_when_change(files, INITIALIZER) { load Rails.root.join('config', 'initializers', 'hot_changes.rb') }
end end

Loading…
Cancel
Save