runger_config 2.6.0
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 +7 -0
- data/CHANGELOG.md +562 -0
- data/LICENSE.txt +22 -0
- data/README.md +1121 -0
- data/lib/anyway/auto_cast.rb +53 -0
- data/lib/anyway/config.rb +473 -0
- data/lib/anyway/dynamic_config.rb +31 -0
- data/lib/anyway/ejson_parser.rb +40 -0
- data/lib/anyway/env.rb +73 -0
- data/lib/anyway/ext/deep_dup.rb +48 -0
- data/lib/anyway/ext/deep_freeze.rb +44 -0
- data/lib/anyway/ext/flatten_names.rb +37 -0
- data/lib/anyway/ext/hash.rb +40 -0
- data/lib/anyway/ext/string_constantize.rb +24 -0
- data/lib/anyway/loaders/base.rb +21 -0
- data/lib/anyway/loaders/doppler.rb +63 -0
- data/lib/anyway/loaders/ejson.rb +89 -0
- data/lib/anyway/loaders/env.rb +18 -0
- data/lib/anyway/loaders/yaml.rb +84 -0
- data/lib/anyway/loaders.rb +79 -0
- data/lib/anyway/option_parser_builder.rb +29 -0
- data/lib/anyway/optparse_config.rb +92 -0
- data/lib/anyway/rails/autoload.rb +42 -0
- data/lib/anyway/rails/config.rb +23 -0
- data/lib/anyway/rails/loaders/credentials.rb +64 -0
- data/lib/anyway/rails/loaders/secrets.rb +37 -0
- data/lib/anyway/rails/loaders/yaml.rb +9 -0
- data/lib/anyway/rails/loaders.rb +5 -0
- data/lib/anyway/rails/settings.rb +83 -0
- data/lib/anyway/rails.rb +24 -0
- data/lib/anyway/railtie.rb +28 -0
- data/lib/anyway/rbs.rb +92 -0
- data/lib/anyway/settings.rb +111 -0
- data/lib/anyway/testing/helpers.rb +36 -0
- data/lib/anyway/testing.rb +13 -0
- data/lib/anyway/tracing.rb +188 -0
- data/lib/anyway/type_casting.rb +144 -0
- data/lib/anyway/utils/deep_merge.rb +21 -0
- data/lib/anyway/utils/which.rb +18 -0
- data/lib/anyway/version.rb +5 -0
- data/lib/anyway.rb +3 -0
- data/lib/anyway_config.rb +54 -0
- 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 +51 -0
- data/lib/generators/anyway/config/templates/config.rb.tt +12 -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 +47 -0
- data/lib/generators/anyway/install/templates/application_config.rb.tt +17 -0
- data/sig/anyway_config.rbs +149 -0
- data/sig/manifest.yml +6 -0
- metadata +202 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module Ext
|
5
|
+
# Add #deep_freeze to hashes and arrays
|
6
|
+
module DeepFreeze
|
7
|
+
refine ::Hash do
|
8
|
+
def deep_freeze
|
9
|
+
freeze
|
10
|
+
each_value do |value|
|
11
|
+
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
refine ::Array do
|
17
|
+
def deep_freeze
|
18
|
+
freeze
|
19
|
+
each do |value|
|
20
|
+
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require "active_support/core_ext/hash/indifferent_access"
|
27
|
+
rescue LoadError
|
28
|
+
end
|
29
|
+
|
30
|
+
if defined?(::ActiveSupport::HashWithIndifferentAccess)
|
31
|
+
refine ::ActiveSupport::HashWithIndifferentAccess do
|
32
|
+
def deep_freeze
|
33
|
+
freeze
|
34
|
+
each_value do |value|
|
35
|
+
value.deep_freeze if value.is_a?(::Hash) || value.is_a?(::Array)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
using self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module Ext
|
5
|
+
# Convert Hash with mixed array and hash values to an
|
6
|
+
# array of paths.
|
7
|
+
module FlattenNames
|
8
|
+
refine ::Array do
|
9
|
+
def flatten_names(prefix, buf)
|
10
|
+
if empty?
|
11
|
+
buf << :"#{prefix}"
|
12
|
+
return buf
|
13
|
+
end
|
14
|
+
|
15
|
+
each_with_object(buf) do |name, acc|
|
16
|
+
if name.is_a?(::Symbol)
|
17
|
+
acc << :"#{prefix}.#{name}"
|
18
|
+
else
|
19
|
+
name.flatten_names(prefix, acc)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
refine ::Hash do
|
26
|
+
def flatten_names(prefix = nil, buf = [])
|
27
|
+
each_with_object(buf) do |(k, v), acc|
|
28
|
+
parent = prefix ? "#{prefix}.#{k}" : k
|
29
|
+
v.flatten_names(parent, acc)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
using self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module Ext
|
5
|
+
# Extend Hash through refinements
|
6
|
+
module Hash
|
7
|
+
refine ::Hash do
|
8
|
+
def stringify_keys!
|
9
|
+
keys.each do |key|
|
10
|
+
value = delete(key)
|
11
|
+
self[key.to_s] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def bury(val, *path)
|
18
|
+
raise ArgumentError, "No path specified" if path.empty?
|
19
|
+
raise ArgumentError, "Path cannot contain nil" if path.compact.size != path.size
|
20
|
+
|
21
|
+
last_key = path.pop
|
22
|
+
hash = path.reduce(self) do |hash, k|
|
23
|
+
hash[k] = {} unless hash.key?(k) && hash[k].is_a?(::Hash)
|
24
|
+
hash[k]
|
25
|
+
end
|
26
|
+
|
27
|
+
hash[last_key] = val
|
28
|
+
end
|
29
|
+
|
30
|
+
def deep_transform_keys(&block)
|
31
|
+
each_with_object({}) do |(key, value), result|
|
32
|
+
result[yield(key)] = value.is_a?(::Hash) ? value.deep_transform_keys(&block) : value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
using self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module Ext
|
5
|
+
# Add simple safe_constantize method to String
|
6
|
+
module StringConstantize
|
7
|
+
refine String do
|
8
|
+
def safe_constantize
|
9
|
+
names = split("::")
|
10
|
+
|
11
|
+
return nil if names.empty?
|
12
|
+
|
13
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
14
|
+
names.shift if names.size > 1 && names.first.empty?
|
15
|
+
|
16
|
+
names.inject(Object) do |constant, name|
|
17
|
+
break if constant.nil?
|
18
|
+
constant.const_get(name, false) if constant.const_defined?(name, false)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
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:).call(**opts)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(local:)
|
15
|
+
@local = local
|
16
|
+
end
|
17
|
+
|
18
|
+
def use_local?() = @local == true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "net/http"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module Anyway
|
8
|
+
using RubyNext
|
9
|
+
|
10
|
+
module Loaders
|
11
|
+
class Doppler < Base
|
12
|
+
class RequestError < StandardError; end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :download_url
|
16
|
+
attr_writer :token
|
17
|
+
|
18
|
+
def token
|
19
|
+
@token || ENV["DOPPLER_TOKEN"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
self.download_url = "https://api.doppler.com/v3/configs/config/secrets/download"
|
24
|
+
|
25
|
+
def call(env_prefix:, **_options)
|
26
|
+
env_payload = parse_doppler_response(url: Doppler.download_url, token: Doppler.token)
|
27
|
+
|
28
|
+
env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast, env_container: env_payload)
|
29
|
+
|
30
|
+
env.fetch_with_trace(env_prefix).then do |(conf, trace)|
|
31
|
+
Tracing.current_trace&.merge!(trace)
|
32
|
+
conf
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def parse_doppler_response(url:, token:)
|
39
|
+
response = fetch_doppler_config(url, token)
|
40
|
+
|
41
|
+
unless response.is_a?(Net::HTTPSuccess)
|
42
|
+
raise RequestError, "#{response.code} #{response.message}"
|
43
|
+
end
|
44
|
+
|
45
|
+
JSON.parse(response.read_body)
|
46
|
+
end
|
47
|
+
|
48
|
+
def fetch_doppler_config(url, token)
|
49
|
+
uri = URI.parse(url)
|
50
|
+
raise "Doppler token is required to load configuration from Doppler" if token.nil?
|
51
|
+
|
52
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
53
|
+
http.use_ssl = true if uri.scheme == "https"
|
54
|
+
|
55
|
+
request = Net::HTTP::Get.new(uri)
|
56
|
+
request["Accept"] = "application/json"
|
57
|
+
request["Authorization"] = "Bearer #{token}"
|
58
|
+
|
59
|
+
http.request(request)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "anyway/ejson_parser"
|
4
|
+
|
5
|
+
module Anyway
|
6
|
+
module Loaders
|
7
|
+
class EJSON < Base
|
8
|
+
class << self
|
9
|
+
attr_accessor :bin_path
|
10
|
+
end
|
11
|
+
|
12
|
+
self.bin_path = "ejson"
|
13
|
+
|
14
|
+
def call(name:, ejson_namespace: name, ejson_parser: Anyway::EJSONParser.new(EJSON.bin_path), **_options)
|
15
|
+
configs = []
|
16
|
+
|
17
|
+
rel_config_paths.each do |rel_config_path|
|
18
|
+
secrets_hash, rel_path =
|
19
|
+
extract_hash_from_rel_config_path(
|
20
|
+
ejson_parser: ejson_parser,
|
21
|
+
rel_config_path: rel_config_path
|
22
|
+
)
|
23
|
+
|
24
|
+
next unless secrets_hash
|
25
|
+
|
26
|
+
config_hash = if ejson_namespace
|
27
|
+
secrets_hash[ejson_namespace]
|
28
|
+
else
|
29
|
+
secrets_hash.except("_public_key")
|
30
|
+
end
|
31
|
+
|
32
|
+
next unless config_hash.is_a?(Hash)
|
33
|
+
|
34
|
+
configs <<
|
35
|
+
trace!(:ejson, path: rel_path) do
|
36
|
+
config_hash
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
return {} if configs.empty?
|
41
|
+
|
42
|
+
configs.inject do |result_config, next_config|
|
43
|
+
Utils.deep_merge!(result_config, next_config)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def rel_config_paths
|
50
|
+
chain = [environmental_rel_config_path]
|
51
|
+
|
52
|
+
chain << "secrets.local.ejson" if use_local?
|
53
|
+
|
54
|
+
chain
|
55
|
+
end
|
56
|
+
|
57
|
+
def environmental_rel_config_path
|
58
|
+
if Settings.current_environment
|
59
|
+
# if environment file is absent, then take data from the default one
|
60
|
+
[
|
61
|
+
"#{Settings.current_environment}/secrets.ejson",
|
62
|
+
default_rel_config_path
|
63
|
+
]
|
64
|
+
else
|
65
|
+
default_rel_config_path
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def default_rel_config_path
|
70
|
+
"secrets.ejson"
|
71
|
+
end
|
72
|
+
|
73
|
+
def extract_hash_from_rel_config_path(ejson_parser:, rel_config_path:)
|
74
|
+
rel_config_path = [rel_config_path] unless rel_config_path.is_a?(Array)
|
75
|
+
|
76
|
+
rel_config_path.each do |rel_conf_path|
|
77
|
+
rel_path = "config/#{rel_conf_path}"
|
78
|
+
abs_path = "#{Settings.app_root}/#{rel_path}"
|
79
|
+
|
80
|
+
result = ejson_parser.call(abs_path)
|
81
|
+
|
82
|
+
return [result, rel_path] if result
|
83
|
+
end
|
84
|
+
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
+
env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast)
|
10
|
+
|
11
|
+
env.fetch_with_trace(env_prefix).then do |(conf, trace)|
|
12
|
+
Tracing.current_trace&.merge!(trace)
|
13
|
+
conf
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,84 @@
|
|
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
|
+
rel_config_path = relative_config_path(config_path).to_s
|
14
|
+
base_config = trace!(:yml, path: rel_config_path) do
|
15
|
+
config = load_base_yml(config_path)
|
16
|
+
environmental?(config) ? config_with_env(config) : config
|
17
|
+
end
|
18
|
+
|
19
|
+
return base_config unless use_local?
|
20
|
+
|
21
|
+
local_path = local_config_path(config_path)
|
22
|
+
local_config = trace!(:yml, path: relative_config_path(local_path).to_s) { load_local_yml(local_path) }
|
23
|
+
Utils.deep_merge!(base_config, local_config)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def environmental?(parsed_yml)
|
29
|
+
# strange, but still possible
|
30
|
+
return true if Settings.default_environmental_key? && parsed_yml.key?(Settings.default_environmental_key)
|
31
|
+
# possible
|
32
|
+
return true if !Settings.future.unwrap_known_environments && Settings.current_environment
|
33
|
+
# for other environments
|
34
|
+
return true if Settings.known_environments&.any? { parsed_yml.key?(_1) }
|
35
|
+
# preferred
|
36
|
+
parsed_yml.key?(Settings.current_environment)
|
37
|
+
end
|
38
|
+
|
39
|
+
def config_with_env(config)
|
40
|
+
env_config = config[Settings.current_environment] || {}
|
41
|
+
return env_config unless Settings.default_environmental_key?
|
42
|
+
|
43
|
+
default_config = config[Settings.default_environmental_key] || {}
|
44
|
+
Utils.deep_merge!(default_config, env_config)
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_yml(path)
|
48
|
+
return {} unless File.file?(path)
|
49
|
+
require "yaml" unless defined?(::YAML)
|
50
|
+
|
51
|
+
# By default, YAML load will return `false` when the yaml document is
|
52
|
+
# empty. When this occurs, we return an empty hash instead, to match
|
53
|
+
# the interface when no config file is present.
|
54
|
+
begin
|
55
|
+
if defined?(ERB)
|
56
|
+
::YAML.load(ERB.new(File.read(path)).result, aliases: true) || {}
|
57
|
+
else
|
58
|
+
::YAML.load_file(path, aliases: true) || {}
|
59
|
+
end
|
60
|
+
rescue ArgumentError
|
61
|
+
if defined?(ERB)
|
62
|
+
::YAML.load(ERB.new(File.read(path)).result) || {}
|
63
|
+
else
|
64
|
+
::YAML.load_file(path) || {}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :load_base_yml, :parse_yml
|
70
|
+
alias_method :load_local_yml, :parse_yml
|
71
|
+
|
72
|
+
def local_config_path(path)
|
73
|
+
path.sub(".yml", ".local.yml")
|
74
|
+
end
|
75
|
+
|
76
|
+
def relative_config_path(path)
|
77
|
+
Pathname.new(path).then do |path|
|
78
|
+
return path if path.relative?
|
79
|
+
path.relative_path_from(Settings.app_root)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -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() = registry.freeze
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def insert_at(index, id, handler)
|
63
|
+
raise ArgumentError, "Loader with ID #{id} has been already registered" unless find(id).nil?
|
64
|
+
|
65
|
+
registry.insert(index, [id, handler])
|
66
|
+
end
|
67
|
+
|
68
|
+
def find(id)
|
69
|
+
registry.find { |(hid, _)| hid == id }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
require "anyway/loaders/base"
|
76
|
+
require "anyway/loaders/yaml"
|
77
|
+
require "anyway/loaders/env"
|
78
|
+
require "anyway/loaders/doppler"
|
79
|
+
require "anyway/loaders/ejson"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module Anyway # :nodoc:
|
6
|
+
# Initializes the OptionParser instance using the given configuration
|
7
|
+
class OptionParserBuilder
|
8
|
+
class << self
|
9
|
+
def call(options)
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
options.each do |key, descriptor|
|
12
|
+
opts.on(*option_parser_on_args(key, **descriptor)) do |val|
|
13
|
+
yield [key, val]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def option_parser_on_args(key, flag: false, desc: nil, type: ::String)
|
22
|
+
on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
|
23
|
+
on_args << type unless flag
|
24
|
+
on_args << desc unless desc.nil?
|
25
|
+
on_args
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "anyway/option_parser_builder"
|
4
|
+
|
5
|
+
require "anyway/ext/deep_dup"
|
6
|
+
|
7
|
+
module Anyway
|
8
|
+
using Anyway::Ext::DeepDup
|
9
|
+
|
10
|
+
# Adds ability to use script options as the source
|
11
|
+
# of configuration (via optparse)
|
12
|
+
module OptparseConfig
|
13
|
+
module ClassMethods
|
14
|
+
def ignore_options(*args)
|
15
|
+
args.each do |name|
|
16
|
+
option_parser_descriptors[name.to_s][:ignore] = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def describe_options(**hargs)
|
21
|
+
hargs.each do |name, desc|
|
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
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def flag_options(*args)
|
31
|
+
args.each do |name|
|
32
|
+
option_parser_descriptors[name.to_s][:flag] = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def extend_options(&block)
|
37
|
+
option_parser_extensions << block
|
38
|
+
end
|
39
|
+
|
40
|
+
def option_parser_options
|
41
|
+
config_attributes.each_with_object({}) do |key, result|
|
42
|
+
descriptor = option_parser_descriptors[key.to_s]
|
43
|
+
next if descriptor[:ignore] == true
|
44
|
+
|
45
|
+
result[key] = descriptor
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def option_parser_extensions
|
50
|
+
return @option_parser_extensions if instance_variable_defined?(:@option_parser_extensions)
|
51
|
+
|
52
|
+
@option_parser_extensions =
|
53
|
+
if superclass < Anyway::Config
|
54
|
+
superclass.option_parser_extensions.dup
|
55
|
+
else
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def option_parser_descriptors
|
61
|
+
return @option_parser_descriptors if instance_variable_defined?(:@option_parser_descriptors)
|
62
|
+
|
63
|
+
@option_parser_descriptors =
|
64
|
+
if superclass < Anyway::Config
|
65
|
+
superclass.option_parser_descriptors.deep_dup
|
66
|
+
else
|
67
|
+
Hash.new { |h, k| h[k] = {} }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def option_parser
|
73
|
+
@option_parser ||= OptionParserBuilder.call(self.class.option_parser_options) do |key, val|
|
74
|
+
write_config_attr(key, val)
|
75
|
+
end.tap do |parser|
|
76
|
+
self.class.option_parser_extensions.map do |extension|
|
77
|
+
extension.call(parser, self)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_options!(options)
|
83
|
+
Tracing.with_trace_source(type: :options) do
|
84
|
+
option_parser.parse!(options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.included(base)
|
89
|
+
base.extend ClassMethods
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This module is used to detect a Rails application and activate the corresponding plugins
|
4
|
+
# when Anyway Config is loaded before Rails (e.g., in config/puma.rb).
|
5
|
+
module Anyway
|
6
|
+
module Rails
|
7
|
+
using RubyNext
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :tracer, :name_method
|
11
|
+
attr_accessor :disable_postponed_load_warning
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def tracepoint_class_callback(event)
|
16
|
+
# Ignore singletons
|
17
|
+
return if event.self.singleton_class?
|
18
|
+
|
19
|
+
# We wait till `rails/application/configuration.rb` has been loaded, since we rely on it
|
20
|
+
# See https://github.com/palkan/anyway_config/issues/134
|
21
|
+
return unless name_method.bind_call(event.self) == "Rails::Application::Configuration"
|
22
|
+
|
23
|
+
tracer.disable
|
24
|
+
|
25
|
+
unless disable_postponed_load_warning
|
26
|
+
warn "Anyway Config was loaded before Rails. Activating Anyway Config Rails plugins now.\n" \
|
27
|
+
"NOTE: Already loaded configs were provisioned without Rails-specific sources."
|
28
|
+
end
|
29
|
+
|
30
|
+
require "anyway/rails"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# TruffleRuby doesn't support TracePoint's end event
|
35
|
+
unless defined?(::TruffleRuby)
|
36
|
+
@tracer = TracePoint.new(:end, &method(:tracepoint_class_callback))
|
37
|
+
@tracer.enable
|
38
|
+
# Use `Module#name` instead of `self.name` to handle overwritten `name` method
|
39
|
+
@name_method = Module.instance_method(:name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/indifferent_access"
|
4
|
+
|
5
|
+
module Anyway
|
6
|
+
module Rails
|
7
|
+
# Enhance config to be more Railsy-like:
|
8
|
+
# – accept hashes with indeferent access
|
9
|
+
# - load data from secrets
|
10
|
+
# - recognize Rails env when loading from YML
|
11
|
+
module Config
|
12
|
+
module ClassMethods
|
13
|
+
# Make defaults to be a Hash with indifferent access
|
14
|
+
def new_empty_config
|
15
|
+
{}.with_indifferent_access
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Anyway::Config.prepend Anyway::Rails::Config
|
23
|
+
Anyway::Config.singleton_class.prepend Anyway::Rails::Config::ClassMethods
|