Dry-configurable is a simple mixin to make Ruby classes configurable. It is quite nice, uses Struct and allows you to have default values. Unfortunately, it does not support (yet hopefully) default values that are evaluated upon each setting key request.
When would that be useful? Let's take a simple example: Kafka + Zookeeper. Zookeeper stores informations about Kafka cluster. They may vary in time, so unless you take them in real time, you might have problem if the cluster were reconfigured. The best option is to set a proc that will be evaluated on each setting request. This is how to obtain such a behavior (note that this will evaluate only default values - if you assign proc in configure block, it will be returned):
# Patch that will allow to use proc based lazy evaluated settings with Dry Configurable
class Dry::Configurable::Config
# @param [Array] All arguments that a Struct accepts
def initialize(*args)
super
setup_dynamics
end
private
# Method that sets up all the proc based lazy evaluated dynamic config values
def setup_dynamics
self.to_h.each do |key, value|
next unless value.is_a?(Proc)
rebuild(key)
end
end
# Method that rebuilds a given accessor, so when it consists a proc value, it will
# evaluate it upon return
# @param method_name [Symbol] name of an accessor that we want to rebuild
def rebuild(method_name)
metaclass = class << self; self; end
metaclass.send(:define_method, method_name) do
super().is_a?(Proc) ? super().call : super()
end
end
end
Example usage:
class Application
extend Dry::Configurable
setting :standard_setting
setting :lazy_assigned
setting :lazy_default, -> { "lazy_default-#{rand}" }
# Note that this works for nested values as well
setting :namespaced do
setting :lazy, -> { "namespaced.lazy-#{rand}" }
end
end
Application.configure do |config|
config.standard_setting = 1
config.lazy_assigned = -> { "lazy_assigned-#{rand}" }
end
Application.config.standard_setting #=> 1
Application.config.standard_setting #=> 1
Application.config.lazy_assigned #=> <Proc>
Application.config.lazy_default #=> "lazy_default-0.9601194173446863"
Application.config.lazy_default #=> "lazy_default-0.9450471949405372"
Application.config.namespaced.lazy #=> "namespaced.lazy-0.6938534114443213"
Application.config.namespaced.lazy #=> "namespaced.lazy-0.747101587617097"