anyway_config 2.0.0.pre2 → 2.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +350 -0
- data/LICENSE.txt +1 -1
- data/README.md +444 -119
- data/lib/.rbnext/2.7/anyway/auto_cast.rb +33 -0
- data/lib/.rbnext/2.7/anyway/config.rb +404 -0
- data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +31 -0
- data/lib/.rbnext/2.7/anyway/tracing.rb +187 -0
- data/lib/anyway/auto_cast.rb +33 -0
- data/lib/anyway/config.rb +235 -58
- data/lib/anyway/dynamic_config.rb +1 -1
- data/lib/anyway/env.rb +29 -19
- data/lib/anyway/ext/hash.rb +14 -6
- data/lib/anyway/loaders.rb +79 -0
- data/lib/anyway/loaders/base.rb +23 -0
- data/lib/anyway/loaders/env.rb +16 -0
- data/lib/anyway/loaders/yaml.rb +46 -0
- data/lib/anyway/option_parser_builder.rb +6 -3
- data/lib/anyway/optparse_config.rb +10 -6
- data/lib/anyway/rails.rb +16 -0
- data/lib/anyway/rails/config.rb +2 -69
- data/lib/anyway/rails/loaders.rb +5 -0
- data/lib/anyway/rails/loaders/credentials.rb +64 -0
- data/lib/anyway/rails/loaders/secrets.rb +39 -0
- data/lib/anyway/rails/loaders/yaml.rb +19 -0
- data/lib/anyway/rails/settings.rb +58 -0
- data/lib/anyway/railtie.rb +14 -2
- data/lib/anyway/settings.rb +29 -0
- data/lib/anyway/tracing.rb +187 -0
- data/lib/anyway/version.rb +1 -1
- data/lib/anyway_config.rb +27 -21
- data/lib/generators/anyway/app_config/USAGE +9 -0
- data/lib/generators/anyway/app_config/app_config_generator.rb +17 -0
- data/lib/generators/anyway/config/USAGE +13 -0
- data/lib/generators/anyway/config/config_generator.rb +46 -0
- data/lib/generators/anyway/config/templates/config.rb.tt +9 -0
- data/lib/generators/anyway/config/templates/config.yml.tt +13 -0
- data/lib/generators/anyway/install/USAGE +4 -0
- data/lib/generators/anyway/install/install_generator.rb +43 -0
- data/lib/generators/anyway/install/templates/application_config.rb.tt +17 -0
- metadata +75 -10
- data/lib/anyway/ext/string_serialize.rb +0 -38
- data/lib/anyway/loaders/env_loader.rb +0 -0
- data/lib/anyway/loaders/secrets_loader.rb +0 -0
- data/lib/anyway/loaders/yaml_loader.rb +0 -0
@@ -16,7 +16,7 @@ module Anyway
|
|
16
16
|
config = allocate
|
17
17
|
options[:env_prefix] ||= name.to_s.upcase
|
18
18
|
options[:config_path] ||= config.resolve_config_path(name, options[:env_prefix])
|
19
|
-
config.load_from_sources(name: name, **options)
|
19
|
+
config.load_from_sources(new_empty_config, name: name, **options)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
data/lib/anyway/env.rb
CHANGED
@@ -4,43 +4,53 @@ module Anyway
|
|
4
4
|
# Parses environment variables and provides
|
5
5
|
# method-like access
|
6
6
|
class Env
|
7
|
+
using RubyNext
|
7
8
|
using Anyway::Ext::DeepDup
|
8
|
-
using Anyway::Ext::
|
9
|
+
using Anyway::Ext::Hash
|
9
10
|
|
10
|
-
|
11
|
+
include Tracing
|
12
|
+
|
13
|
+
attr_reader :data, :traces, :type_cast
|
14
|
+
|
15
|
+
def initialize(type_cast: AutoCast)
|
16
|
+
@type_cast = type_cast
|
11
17
|
@data = {}
|
18
|
+
@traces = {}
|
12
19
|
end
|
13
20
|
|
14
21
|
def clear
|
15
|
-
|
22
|
+
data.clear
|
23
|
+
traces.clear
|
16
24
|
end
|
17
25
|
|
18
26
|
def fetch(prefix)
|
19
|
-
|
20
|
-
|
27
|
+
return data[prefix].deep_dup if data.key?(prefix)
|
28
|
+
|
29
|
+
Tracing.capture do
|
30
|
+
data[prefix] = parse_env(prefix)
|
31
|
+
end.then do |trace|
|
32
|
+
traces[prefix] = trace
|
33
|
+
end
|
34
|
+
|
35
|
+
data[prefix].deep_dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_with_trace(prefix)
|
39
|
+
[fetch(prefix), traces[prefix]]
|
21
40
|
end
|
22
41
|
|
23
42
|
private
|
24
43
|
|
25
44
|
def parse_env(prefix)
|
45
|
+
match_prefix = "#{prefix}_"
|
26
46
|
ENV.each_pair.with_object({}) do |(key, val), data|
|
27
|
-
next unless key.start_with?(
|
47
|
+
next unless key.start_with?(match_prefix)
|
28
48
|
|
29
49
|
path = key.sub(/^#{prefix}_/, "").downcase
|
30
|
-
set_by_path(data, path, val.serialize)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def set_by_path(to, path, val)
|
35
|
-
parts = path.split("__")
|
36
|
-
|
37
|
-
to = get_hash(to, parts.shift) while parts.length > 1
|
38
50
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def get_hash(from, name)
|
43
|
-
(from[name] ||= {})
|
51
|
+
paths = path.split("__")
|
52
|
+
trace!(:env, *paths, key: key) { data.bury(type_cast.call(val), *paths) }
|
53
|
+
end
|
44
54
|
end
|
45
55
|
end
|
46
56
|
end
|
data/lib/anyway/ext/hash.rb
CHANGED
@@ -7,18 +7,14 @@ module Anyway
|
|
7
7
|
refine ::Hash do
|
8
8
|
# From ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_merge
|
9
9
|
def deep_merge!(other_hash)
|
10
|
-
other_hash
|
11
|
-
this_value = self[current_key]
|
12
|
-
|
10
|
+
merge!(other_hash) do |key, this_value, other_value|
|
13
11
|
if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
|
14
12
|
this_value.deep_merge!(other_value)
|
15
13
|
this_value
|
16
14
|
else
|
17
|
-
|
15
|
+
other_value
|
18
16
|
end
|
19
17
|
end
|
20
|
-
|
21
|
-
self
|
22
18
|
end
|
23
19
|
|
24
20
|
def stringify_keys!
|
@@ -30,6 +26,18 @@ module Anyway
|
|
30
26
|
|
31
27
|
self
|
32
28
|
end
|
29
|
+
|
30
|
+
def bury(val, *path)
|
31
|
+
raise ArgumentError, "No path specified" if path.empty?
|
32
|
+
raise ArgumentError, "Path cannot contain nil" if path.compact.size != path.size
|
33
|
+
|
34
|
+
last_key = path.pop
|
35
|
+
hash = path.reduce(self) do |hash, k|
|
36
|
+
hash[k] = {} unless hash.key?(k)
|
37
|
+
hash[k]
|
38
|
+
end
|
39
|
+
hash[last_key] = val
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
43
|
using self
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
using RubyNext
|
5
|
+
|
6
|
+
module Loaders
|
7
|
+
class Registry
|
8
|
+
attr_reader :registry
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@registry = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def prepend(id, handler = nil, &block)
|
15
|
+
handler ||= block
|
16
|
+
insert_at(0, id, handler)
|
17
|
+
end
|
18
|
+
|
19
|
+
def append(id, handler = nil, &block)
|
20
|
+
handler ||= block
|
21
|
+
insert_at(registry.size, id, handler)
|
22
|
+
end
|
23
|
+
|
24
|
+
def insert_before(another_id, id, handler = nil, &block)
|
25
|
+
ind = registry.find_index { |(hid, _)| hid == another_id }
|
26
|
+
raise ArgumentError, "Loader with ID #{another_id} hasn't been registered" if ind.nil?
|
27
|
+
|
28
|
+
handler ||= block
|
29
|
+
insert_at(ind, id, handler)
|
30
|
+
end
|
31
|
+
|
32
|
+
def insert_after(another_id, id, handler = nil, &block)
|
33
|
+
ind = registry.find_index { |(hid, _)| hid == another_id }
|
34
|
+
raise ArgumentError, "Loader with ID #{another_id} hasn't been registered" if ind.nil?
|
35
|
+
|
36
|
+
handler ||= block
|
37
|
+
insert_at(ind + 1, id, handler)
|
38
|
+
end
|
39
|
+
|
40
|
+
def override(id, handler)
|
41
|
+
find(id).then do |id_to_handler|
|
42
|
+
raise ArgumentError, "Loader with ID #{id} hasn't been registered" if id_to_handler.nil?
|
43
|
+
id_to_handler[1] = handler
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(id)
|
48
|
+
find(id).then do |id_to_handler|
|
49
|
+
raise ArgumentError, "Loader with ID #{id} hasn't been registered" if id_to_handler.nil?
|
50
|
+
registry.delete id_to_handler
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def each(&block)
|
55
|
+
registry.each(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def freeze
|
59
|
+
registry.freeze
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def insert_at(index, id, handler)
|
65
|
+
raise ArgumentError, "Loader with ID #{id} has been already registered" unless find(id).nil?
|
66
|
+
|
67
|
+
registry.insert(index, [id, handler])
|
68
|
+
end
|
69
|
+
|
70
|
+
def find(id)
|
71
|
+
registry.find { |(hid, _)| hid == id }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require "anyway/loaders/base"
|
78
|
+
require "anyway/loaders/yaml"
|
79
|
+
require "anyway/loaders/env"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module Loaders
|
5
|
+
class Base
|
6
|
+
include Tracing
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def call(local: Anyway::Settings.use_local_files, **opts)
|
10
|
+
new(local: local).call(**opts)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(local:)
|
15
|
+
@local = local
|
16
|
+
end
|
17
|
+
|
18
|
+
def use_local?
|
19
|
+
@local == true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
using RubyNext
|
5
|
+
|
6
|
+
module Loaders
|
7
|
+
class Env < Base
|
8
|
+
def call(env_prefix:, **_options)
|
9
|
+
Anyway.env.fetch_with_trace(env_prefix).then do |(conf, trace)|
|
10
|
+
Tracing.current_trace&.merge!(trace)
|
11
|
+
conf
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "anyway/ext/hash"
|
5
|
+
|
6
|
+
using RubyNext
|
7
|
+
using Anyway::Ext::Hash
|
8
|
+
|
9
|
+
module Anyway
|
10
|
+
module Loaders
|
11
|
+
class YAML < Base
|
12
|
+
def call(config_path:, **_options)
|
13
|
+
load_yml(config_path).tap do |config|
|
14
|
+
config.deep_merge!(load_yml(local_config_path(config_path))) if use_local?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load_yml(path)
|
21
|
+
trace!(:yml, path: relative_config_path(path).to_s) { parse_yml(path) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_yml(path)
|
25
|
+
return {} unless File.file?(path)
|
26
|
+
require "yaml" unless defined?(::YAML)
|
27
|
+
if defined?(ERB)
|
28
|
+
::YAML.safe_load(ERB.new(File.read(path)).result, [], [], true)
|
29
|
+
else
|
30
|
+
::YAML.load_file(path)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def local_config_path(path)
|
35
|
+
path.sub(/\.yml/, ".local.yml")
|
36
|
+
end
|
37
|
+
|
38
|
+
def relative_config_path(path)
|
39
|
+
Pathname.new(path).then do |path|
|
40
|
+
return path if path.relative?
|
41
|
+
path.relative_path_from(Pathname.new(Dir.pwd))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -8,9 +8,11 @@ module Anyway # :nodoc:
|
|
8
8
|
class << self
|
9
9
|
def call(options)
|
10
10
|
OptionParser.new do |opts|
|
11
|
+
opts.accept(AutoCast) { AutoCast.call(_1) }
|
12
|
+
|
11
13
|
options.each do |key, descriptor|
|
12
|
-
opts.on(*option_parser_on_args(key, **descriptor)) do |
|
13
|
-
yield [key,
|
14
|
+
opts.on(*option_parser_on_args(key, **descriptor)) do |val|
|
15
|
+
yield [key, val]
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -18,8 +20,9 @@ module Anyway # :nodoc:
|
|
18
20
|
|
19
21
|
private
|
20
22
|
|
21
|
-
def option_parser_on_args(key, flag: false, desc: nil)
|
23
|
+
def option_parser_on_args(key, flag: false, desc: nil, type: AutoCast)
|
22
24
|
on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
|
25
|
+
on_args << type unless flag
|
23
26
|
on_args << desc unless desc.nil?
|
24
27
|
on_args
|
25
28
|
end
|
@@ -3,11 +3,9 @@
|
|
3
3
|
require "anyway/option_parser_builder"
|
4
4
|
|
5
5
|
require "anyway/ext/deep_dup"
|
6
|
-
require "anyway/ext/string_serialize"
|
7
6
|
|
8
7
|
module Anyway
|
9
8
|
using Anyway::Ext::DeepDup
|
10
|
-
using Anyway::Ext::StringSerialize
|
11
9
|
|
12
10
|
# Adds ability to use script options as the source
|
13
11
|
# of configuration (via optparse)
|
@@ -21,7 +19,11 @@ module Anyway
|
|
21
19
|
|
22
20
|
def describe_options(**hargs)
|
23
21
|
hargs.each do |name, desc|
|
24
|
-
|
22
|
+
if String === desc
|
23
|
+
option_parser_descriptors[name.to_s][:desc] = desc
|
24
|
+
else
|
25
|
+
option_parser_descriptors[name.to_s].merge!(desc)
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -69,8 +71,8 @@ module Anyway
|
|
69
71
|
|
70
72
|
def option_parser
|
71
73
|
@option_parser ||= begin
|
72
|
-
OptionParserBuilder.call(self.class.option_parser_options) do |key,
|
73
|
-
|
74
|
+
OptionParserBuilder.call(self.class.option_parser_options) do |key, val|
|
75
|
+
write_config_attr(key, val)
|
74
76
|
end.tap do |parser|
|
75
77
|
self.class.option_parser_extensions.map do |extension|
|
76
78
|
extension.call(parser, self)
|
@@ -80,7 +82,9 @@ module Anyway
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def parse_options!(options)
|
83
|
-
|
85
|
+
Tracing.with_trace_source(type: :options) do
|
86
|
+
option_parser.parse!(options)
|
87
|
+
end
|
84
88
|
end
|
85
89
|
|
86
90
|
def self.included(base)
|
data/lib/anyway/rails.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module Rails
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require "anyway/rails/settings"
|
9
|
+
require "anyway/rails/config"
|
10
|
+
require "anyway/rails/loaders"
|
11
|
+
require "anyway/railtie"
|
12
|
+
|
13
|
+
# Configure Rails loaders
|
14
|
+
Anyway.loaders.override :yml, Anyway::Rails::Loaders::YAML
|
15
|
+
Anyway.loaders.insert_after :yml, :secrets, Anyway::Rails::Loaders::Secrets
|
16
|
+
Anyway.loaders.insert_after :secrets, :credentials, Anyway::Rails::Loaders::Credentials
|
data/lib/anyway/rails/config.rb
CHANGED
@@ -11,76 +11,9 @@ module Anyway
|
|
11
11
|
module Config
|
12
12
|
module ClassMethods
|
13
13
|
# Make defaults to be a Hash with indifferent access
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
@defaults = super.with_indifferent_access
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def load_from_sources(**options)
|
22
|
-
base_config = {}.with_indifferent_access
|
23
|
-
each_source(options) do |config|
|
24
|
-
base_config.deep_merge!(config) if config
|
25
|
-
end
|
26
|
-
base_config
|
27
|
-
end
|
28
|
-
|
29
|
-
def each_source(options)
|
30
|
-
yield load_from_file(options)
|
31
|
-
yield load_from_secrets(options)
|
32
|
-
yield load_from_credentials(options)
|
33
|
-
yield load_from_env(options)
|
34
|
-
end
|
35
|
-
|
36
|
-
def load_from_file(name:, config_path:, env_prefix:, **_options)
|
37
|
-
file_config = load_from_yml(config_path)[::Rails.env] || {}
|
38
|
-
|
39
|
-
if Anyway::Settings.use_local_files
|
40
|
-
local_config_path = config_path.sub(/\.yml/, ".local.yml")
|
41
|
-
file_config.deep_merge!(load_from_yml(local_config_path) || {})
|
14
|
+
def new_empty_config
|
15
|
+
{}.with_indifferent_access
|
42
16
|
end
|
43
|
-
|
44
|
-
file_config
|
45
|
-
end
|
46
|
-
|
47
|
-
def load_from_secrets(name:, **_options)
|
48
|
-
return unless ::Rails.application.respond_to?(:secrets)
|
49
|
-
|
50
|
-
::Rails.application.secrets.public_send(name)
|
51
|
-
end
|
52
|
-
|
53
|
-
def load_from_credentials(name:, **_options)
|
54
|
-
# do not load from credentials if we're in the context
|
55
|
-
# of the `credentials:edit` command
|
56
|
-
return if defined?(::Rails::Command::CredentialsCommand)
|
57
|
-
|
58
|
-
return unless ::Rails.application.respond_to?(:credentials)
|
59
|
-
|
60
|
-
# Create a new hash cause credentials are mutable!
|
61
|
-
creds_config = {}
|
62
|
-
|
63
|
-
creds_config.deep_merge!(::Rails.application.credentials.public_send(name) || {})
|
64
|
-
|
65
|
-
creds_config.deep_merge!(load_from_local_credentials(name: name) || {}) if Anyway::Settings.use_local_files
|
66
|
-
creds_config
|
67
|
-
end
|
68
|
-
|
69
|
-
def load_from_local_credentials(name:)
|
70
|
-
local_creds_path = ::Rails.root.join("config/credentials/local.yml.enc").to_s
|
71
|
-
|
72
|
-
return unless File.file?(local_creds_path)
|
73
|
-
|
74
|
-
creds = ::Rails.application.encrypted(
|
75
|
-
local_creds_path,
|
76
|
-
key_path: ::Rails.root.join("config/credentials/local.key")
|
77
|
-
)
|
78
|
-
|
79
|
-
creds.public_send(name)
|
80
|
-
end
|
81
|
-
|
82
|
-
def default_config_path(name)
|
83
|
-
::Rails.root.join("config", "#{name}.yml")
|
84
17
|
end
|
85
18
|
end
|
86
19
|
end
|