lib/anyway/config.rb in anyway_config-1.4.4 vs lib/anyway/config.rb in anyway_config-2.0.0.pre
- old
+ new
@@ -1,14 +1,16 @@
# frozen_string_literal: true
-require 'anyway/ext/jruby' if defined? JRUBY_VERSION
-require 'anyway/ext/deep_dup'
-require 'anyway/ext/deep_freeze'
-require 'anyway/ext/hash'
-require 'anyway/ext/string_serialize'
-require 'anyway/option_parser_builder'
+require "anyway/optparse_config"
+require "anyway/dynamic_config"
+require "anyway/ext/jruby" if defined? JRUBY_VERSION
+require "anyway/ext/deep_dup"
+require "anyway/ext/deep_freeze"
+require "anyway/ext/hash"
+require "anyway/ext/string_serialize"
+
module Anyway # :nodoc:
if defined? JRUBY_VERSION
using Anyway::Ext::JRuby
else
using Anyway::Ext::DeepDup
@@ -19,112 +21,120 @@
# Base config class
# Provides `attr_config` method to describe
# configuration parameters and set defaults
class Config
- class << self
- attr_reader :defaults, :config_attributes, :option_parser_extension
+ include OptparseConfig
+ include DynamicConfig
+ class << self
def attr_config(*args, **hargs)
- @defaults ||= {}
- @config_attributes ||= []
-
new_defaults = hargs.deep_dup
new_defaults.stringify_keys!
+
defaults.merge! new_defaults
new_keys = (args + new_defaults.keys) - config_attributes
- @config_attributes += new_keys
+ config_attributes.push(*new_keys)
attr_accessor(*new_keys)
end
- def config_name(val = nil)
- return (@config_name = val.to_s) unless val.nil?
+ def defaults
+ return @defaults if instance_variable_defined?(:@defaults)
- @config_name = underscore_name unless defined?(@config_name)
- @config_name
+ @defaults =
+ if superclass < Anyway::Config
+ superclass.defaults.deep_dup
+ else
+ {}
+ end
end
- def ignore_options(*args)
- args.each do |name|
- option_parser_descriptors[name.to_s][:ignore] = true
- end
- end
+ def config_attributes
+ return @config_attributes if instance_variable_defined?(:@config_attributes)
- def describe_options(**hargs)
- hargs.each do |name, desc|
- option_parser_descriptors[name.to_s][:desc] = desc
- end
+ @config_attributes =
+ if superclass < Anyway::Config
+ superclass.config_attributes.dup
+ else
+ []
+ end
end
- def flag_options(*args)
- args.each do |name|
- option_parser_descriptors[name.to_s][:flag] = true
- end
- end
+ def config_name(val = nil)
+ return (@explicit_config_name = val.to_s) unless val.nil?
- def extend_options(&block)
- @option_parser_extension = block
+ return @config_name if instance_variable_defined?(:@config_name)
+
+ @config_name = explicit_config_name || build_config_name
end
- def option_parser_options
- config_attributes.each_with_object({}) do |key, result|
- descriptor = option_parser_descriptors[key.to_s]
- next if descriptor[:ignore] == true
+ def explicit_config_name
+ return @explicit_config_name if instance_variable_defined?(:@explicit_config_name)
- result[key] = descriptor
- end
+ @explicit_config_name =
+ if superclass.respond_to?(:explicit_config_name)
+ superclass.explicit_config_name
+ end
end
+ def explicit_config_name?
+ !explicit_config_name.nil?
+ end
+
def env_prefix(val = nil)
- return (@env_prefix = val.to_s) unless val.nil?
+ return (@env_prefix = val.to_s.upcase) unless val.nil?
- @env_prefix
- end
+ return @env_prefix if instance_variable_defined?(:@env_prefix)
- # Load config as Hash by any name
- #
- # Example:
- #
- # my_config = Anyway::Config.for(:my_app)
- # # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
- def for(name)
- new(name: name, load: false).load_from_sources
+ @env_prefix =
+ if superclass < Anyway::Config && superclass.explicit_config_name?
+ superclass.env_prefix
+ else
+ config_name.upcase
+ end
end
private
- def option_parser_descriptors
- @option_parser_descriptors ||= Hash.new { |h, k| h[k] = {} }
- end
+ def build_config_name
+ unless name
+ raise "Please, specify config name explicitly for anonymous class " \
+ "via `config_name :my_config`"
+ end
- def underscore_name
- return unless name
+ # handle two cases:
+ # - SomeModule::Config => "some_module"
+ # - SomeConfig => "some"
+ unless name =~ /^(\w+)(\:\:)?Config$/
+ raise "Couldn't infer config name, please, specify it explicitly" \
+ "via `config_name :my_config`"
+ end
- word = name[/^(\w+)/]
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
- word.downcase!
- word
+ Regexp.last_match[1].tap(&:downcase!)
end
end
attr_reader :config_name, :env_prefix
- # Instantiate config with specified name, loads the data and applies overrides
+ # Instantiate config instance.
#
# Example:
#
- # my_config = Anyway::Config.new(name: :my_app, load: true, overrides: { some: :value })
+ # my_config = Anyway::Config.new()
#
- def initialize(name: nil, load: true, overrides: {})
- @config_name = name || self.class.config_name
+ # # provide some values explicitly
+ # my_config = Anyway::Config.new({some: :value})
+ #
+ def initialize(overrides = {})
+ @config_name = self.class.config_name
raise ArgumentError, "Config name is missing" unless @config_name
- @env_prefix = self.class.env_prefix || @config_name
+ @env_prefix = self.class.env_prefix
- self.load(overrides) if load
+ load(overrides)
end
def reload(overrides = {})
clear
load(overrides)
@@ -137,74 +147,82 @@
end
self
end
def load(overrides = {})
- config = load_from_sources((self.class.defaults || {}).deep_dup)
+ base_config = (self.class.defaults || {}).deep_dup
- config.merge!(overrides) unless overrides.nil?
- config.each do |key, val|
+ base_config.deep_merge!(
+ load_from_sources(
+ name: config_name,
+ env_prefix: env_prefix,
+ config_path: resolve_config_path(config_name, env_prefix)
+ )
+ )
+
+ base_config.merge!(overrides) unless overrides.nil?
+
+ base_config.each do |key, val|
set_value(key, val)
end
end
- def load_from_sources(config = {})
- # Handle anonymous configs
- return config unless config_name
-
- load_from_file(config)
- load_from_env(config)
+ def load_from_sources(**options)
+ base_config = {}
+ each_source(options) do |config|
+ base_config.deep_merge!(config) if config
+ end
+ base_config
end
- def load_from_file(config)
- config.deep_merge!(parse_yml(config_path) || {}) if config_path && File.file?(config_path)
- config
+ def each_source(options)
+ yield load_from_file(options)
+ yield load_from_env(options)
end
- def load_from_env(config)
- config.deep_merge!(env_part)
- config
- end
+ def load_from_file(name:, env_prefix:, config_path:, **_options)
+ file_config = load_from_yml(config_path)
- def option_parser
- @option_parser ||= begin
- parser = OptionParserBuilder.call(self.class.option_parser_options) do |key, arg|
- set_value(key, arg.is_a?(String) ? arg.serialize : arg)
- end
- self.class.option_parser_extension&.call(parser, self) || parser
+ if Anyway::Settings.use_local_files
+ local_config_path = config_path.sub(/\.yml/, ".local.yml")
+ file_config.deep_merge!(load_from_yml(local_config_path))
end
+
+ file_config
end
- def parse_options!(options)
- option_parser.parse!(options)
+ def load_from_env(name:, env_prefix:, **_options)
+ Anyway.env.fetch(env_prefix)
end
def to_h
self.class.config_attributes.each_with_object({}) do |key, obj|
obj[key.to_sym] = send(key)
end.deep_dup.deep_freeze
end
+ def resolve_config_path(name, env_prefix)
+ Anyway.env.fetch(env_prefix).delete("conf") || default_config_path(name)
+ end
+
private
- def env_part
- Anyway.env.fetch(env_prefix)
+ def set_value(key, val)
+ send("#{key}=", val) if respond_to?(key)
end
- def config_path
- env_part.delete('conf') || default_config_path
- end
+ def load_from_yml(path)
+ return {} unless File.file?(path)
- def default_config_path
- "./config/#{config_name}.yml"
+ parse_yml(path)
end
- def set_value(key, val)
- send("#{key}=", val) if respond_to?(key)
+ def default_config_path(name)
+ "./config/#{name}.yml"
end
def parse_yml(path)
- require 'yaml'
+ require "yaml"
if defined?(ERB)
YAML.safe_load(ERB.new(File.read(path)).result, [], [], true)
else
YAML.load_file(path)
end