1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
require 'dry/core/constants'
require 'dry/configurable/settings'
require 'dry/configurable/error'
require 'dry/configurable/version'
# A collection of micro-libraries, each intended to encapsulate
# a common task in Ruby
module Dry
# A simple configuration mixin
#
# @example class-level configuration
#
# class App
# extend Dry::Configurable
#
# setting :database do
# setting :dsn, 'sqlite:memory'
# end
# end
#
# App.config.database.dsn = 'jdbc:sqlite:memory'
# App.config.database.dsn
# # => "jdbc:sqlite:memory"
#
# @example instance-level configuration
#
# class App
# include Dry::Configurable
#
# setting :database
# end
#
# production = App.new
# production.config.database = ENV['DATABASE_URL']
# production.finalize!
#
# development = App.new
# development.config.database = 'jdbc:sqlite:memory'
# development.finalize!
#
# @api public
module Configurable
include Dry::Core::Constants
module ClassMethods
# @private
def self.extended(base)
base.instance_exec do
@settings = Settings.new
end
end
# Add a setting to the configuration
#
# @param [Mixed] key
# The accessor key for the configuration value
# @param [Mixed] default
# The default config value
#
# @yield
# If a block is given, it will be evaluated in the context of
# a new configuration class, and bound as the default value
#
# @return [Dry::Configurable::Config]
#
# @api public
def setting(key, value = Undefined, options = Undefined, &block)
raise_already_defined_config(key) if _settings.config_defined?
setting = _settings.add(key, value, options, &block)
if setting.reader?
readers = singleton_class < Configurable ? singleton_class : self
readers.send(:define_method, setting.name) { config[setting.name] }
end
end
# Return an array of setting names
#
# @return [Set]
#
# @api public
def settings
_settings.names
end
# @private no, really...
def _settings
@settings
end
private
# @private
def raise_already_defined_config(key)
raise AlreadyDefinedConfig,
"Cannot add setting +#{key}+, #{self} is already configured"
end
# @private
def inherited(subclass)
parent = self
subclass.instance_exec do
@settings = parent._settings.dup
end
if singleton_class < Configurable
parent_config = @config
subclass.instance_exec do
@config = _settings.create_config
@config.define!(parent_config.to_h) if parent_config.defined?
end
end
super
end
end
class << self
# @private
def extended(base)
base.extend(ClassMethods)
base.class_eval do
@config = _settings.create_config
end
end
# @private
def included(base)
base.extend(ClassMethods)
end
end
# @private
def initialize(*)
@config = self.class._settings.create_config
super
end
# Return configuration
#
# @return [Dry::Configurable::Config]
#
# @api public
def config
return @config if @config.defined?
@config.define!
end
# Return configuration
#
# @yield [Dry::Configuration::Config]
#
# @return [Dry::Configurable::Config]
#
# @api public
def configure
raise FrozenConfig, 'Cannot modify frozen config' if frozen?
yield(config) if block_given?
self
end
# Finalize and freeze configuration
#
# @return [Dry::Configurable::Config]
#
# @api public
def finalize!
freeze
config.finalize!
end
# @api public
def dup
super.tap do |copy|
copy.instance_variable_set(:@config, config.dup)
end
end
# @api public
def clone
if frozen?
super
else
super.tap do |copy|
copy.instance_variable_set(:@config, config.dup)
end
end
end
end
end
|