runger_config 3.0.1 → 5.0.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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -35
  3. data/README.md +83 -83
  4. data/lib/generators/runger/app_config/app_config_generator.rb +13 -0
  5. data/lib/generators/runger/config/config_generator.rb +49 -0
  6. data/lib/generators/runger/install/install_generator.rb +45 -0
  7. data/lib/generators/{anyway → runger}/install/templates/application_config.rb.tt +1 -1
  8. data/lib/{anyway → runger}/auto_cast.rb +4 -4
  9. data/lib/{anyway → runger}/config.rb +124 -104
  10. data/lib/runger/dynamic_config.rb +29 -0
  11. data/lib/runger/ejson_parser.rb +40 -0
  12. data/lib/runger/env.rb +70 -0
  13. data/lib/runger/ext/deep_dup.rb +45 -0
  14. data/lib/runger/ext/deep_freeze.rb +40 -0
  15. data/lib/runger/ext/flatten_names.rb +33 -0
  16. data/lib/runger/ext/hash.rb +37 -0
  17. data/lib/runger/ext/string_constantize.rb +21 -0
  18. data/lib/runger/loaders/base.rb +17 -0
  19. data/lib/runger/loaders/doppler.rb +57 -0
  20. data/lib/runger/loaders/ejson.rb +91 -0
  21. data/lib/runger/loaders/env.rb +12 -0
  22. data/lib/runger/loaders/yaml.rb +86 -0
  23. data/lib/runger/loaders.rb +75 -0
  24. data/lib/runger/option_parser_builder.rb +27 -0
  25. data/lib/{anyway → runger}/optparse_config.rb +15 -14
  26. data/lib/runger/rails/autoload.rb +38 -0
  27. data/lib/runger/rails/config.rb +19 -0
  28. data/lib/runger/rails/loaders/credentials.rb +58 -0
  29. data/lib/runger/rails/loaders/secrets.rb +31 -0
  30. data/lib/runger/rails/loaders/yaml.rb +4 -0
  31. data/lib/runger/rails/loaders.rb +5 -0
  32. data/lib/runger/rails/settings.rb +83 -0
  33. data/lib/runger/rails.rb +22 -0
  34. data/lib/{anyway → runger}/railtie.rb +9 -8
  35. data/lib/{anyway → runger}/rbs.rb +30 -30
  36. data/lib/runger/settings.rb +109 -0
  37. data/lib/runger/testing/helpers.rb +34 -0
  38. data/lib/{anyway → runger}/testing.rb +3 -3
  39. data/lib/runger/tracing.rb +195 -0
  40. data/lib/{anyway → runger}/type_casting.rb +19 -14
  41. data/lib/{anyway → runger}/utils/deep_merge.rb +2 -2
  42. data/lib/runger/utils/which.rb +16 -0
  43. data/lib/runger/version.rb +5 -0
  44. data/lib/{anyway.rb → runger.rb} +1 -1
  45. data/lib/runger_config.rb +56 -0
  46. data/sig/{anyway_config.rbs → runger_config.rbs} +1 -1
  47. metadata +68 -68
  48. data/lib/anyway/dynamic_config.rb +0 -31
  49. data/lib/anyway/ejson_parser.rb +0 -40
  50. data/lib/anyway/env.rb +0 -72
  51. data/lib/anyway/ext/deep_dup.rb +0 -48
  52. data/lib/anyway/ext/deep_freeze.rb +0 -44
  53. data/lib/anyway/ext/flatten_names.rb +0 -37
  54. data/lib/anyway/ext/hash.rb +0 -40
  55. data/lib/anyway/ext/string_constantize.rb +0 -24
  56. data/lib/anyway/loaders/base.rb +0 -21
  57. data/lib/anyway/loaders/doppler.rb +0 -61
  58. data/lib/anyway/loaders/ejson.rb +0 -89
  59. data/lib/anyway/loaders/env.rb +0 -16
  60. data/lib/anyway/loaders/yaml.rb +0 -83
  61. data/lib/anyway/loaders.rb +0 -77
  62. data/lib/anyway/option_parser_builder.rb +0 -29
  63. data/lib/anyway/rails/autoload.rb +0 -40
  64. data/lib/anyway/rails/config.rb +0 -23
  65. data/lib/anyway/rails/loaders/credentials.rb +0 -62
  66. data/lib/anyway/rails/loaders/secrets.rb +0 -35
  67. data/lib/anyway/rails/loaders/yaml.rb +0 -9
  68. data/lib/anyway/rails/loaders.rb +0 -5
  69. data/lib/anyway/rails/settings.rb +0 -83
  70. data/lib/anyway/rails.rb +0 -24
  71. data/lib/anyway/settings.rb +0 -111
  72. data/lib/anyway/testing/helpers.rb +0 -36
  73. data/lib/anyway/tracing.rb +0 -188
  74. data/lib/anyway/utils/which.rb +0 -18
  75. data/lib/anyway/version.rb +0 -5
  76. data/lib/anyway_config.rb +0 -49
  77. data/lib/generators/anyway/app_config/app_config_generator.rb +0 -17
  78. data/lib/generators/anyway/config/config_generator.rb +0 -46
  79. data/lib/generators/anyway/install/install_generator.rb +0 -47
  80. /data/lib/generators/{anyway → runger}/app_config/USAGE +0 -0
  81. /data/lib/generators/{anyway → runger}/config/USAGE +0 -0
  82. /data/lib/generators/{anyway → runger}/config/templates/config.rb.tt +0 -0
  83. /data/lib/generators/{anyway → runger}/config/templates/config.yml.tt +0 -0
  84. /data/lib/generators/{anyway → runger}/install/USAGE +0 -0
@@ -1,40 +0,0 @@
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
@@ -1,24 +0,0 @@
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
@@ -1,21 +0,0 @@
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, **)
10
- new(local:).call(**)
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
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "uri"
4
- require "net/http"
5
- require "json"
6
-
7
- module Anyway
8
- module Loaders
9
- class Doppler < Base
10
- class RequestError < StandardError; end
11
-
12
- class << self
13
- attr_accessor :download_url
14
- attr_writer :token
15
-
16
- def token
17
- @token || ENV["DOPPLER_TOKEN"]
18
- end
19
- end
20
-
21
- self.download_url = "https://api.doppler.com/v3/configs/config/secrets/download"
22
-
23
- def call(env_prefix:, **_options)
24
- env_payload = parse_doppler_response(url: Doppler.download_url, token: Doppler.token)
25
-
26
- env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast, env_container: env_payload)
27
-
28
- env.fetch_with_trace(env_prefix).then do |(conf, trace)|
29
- Tracing.current_trace&.merge!(trace)
30
- conf
31
- end
32
- end
33
-
34
- private
35
-
36
- def parse_doppler_response(url:, token:)
37
- response = fetch_doppler_config(url, token)
38
-
39
- unless response.is_a?(Net::HTTPSuccess)
40
- raise RequestError, "#{response.code} #{response.message}"
41
- end
42
-
43
- JSON.parse(response.read_body)
44
- end
45
-
46
- def fetch_doppler_config(url, token)
47
- uri = URI.parse(url)
48
- raise "Doppler token is required to load configuration from Doppler" if token.nil?
49
-
50
- http = Net::HTTP.new(uri.host, uri.port)
51
- http.use_ssl = true if uri.scheme == "https"
52
-
53
- request = Net::HTTP::Get.new(uri)
54
- request["Accept"] = "application/json"
55
- request["Authorization"] = "Bearer #{token}"
56
-
57
- http.request(request)
58
- end
59
- end
60
- end
61
- end
@@ -1,89 +0,0 @@
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
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anyway
4
- module Loaders
5
- class Env < Base
6
- def call(env_prefix:, **_options)
7
- env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast)
8
-
9
- 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
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pathname"
4
- require "anyway/ext/hash"
5
-
6
- using Anyway::Ext::Hash
7
-
8
- module Anyway
9
- module Loaders
10
- class YAML < Base
11
- def call(config_path:, **_options)
12
- rel_config_path = relative_config_path(config_path).to_s
13
- base_config = trace!(:yml, path: rel_config_path) do
14
- config = load_base_yml(config_path)
15
- environmental?(config) ? config_with_env(config) : config
16
- end
17
-
18
- return base_config unless use_local?
19
-
20
- local_path = local_config_path(config_path)
21
- local_config = trace!(:yml, path: relative_config_path(local_path).to_s) { load_local_yml(local_path) }
22
- Utils.deep_merge!(base_config, local_config)
23
- end
24
-
25
- private
26
-
27
- def environmental?(parsed_yml)
28
- # strange, but still possible
29
- return true if Settings.default_environmental_key? && parsed_yml.key?(Settings.default_environmental_key)
30
- # possible
31
- return true if !Settings.future.unwrap_known_environments && Settings.current_environment
32
- # for other environments
33
- return true if Settings.known_environments&.any? { parsed_yml.key?(_1) }
34
- # preferred
35
- parsed_yml.key?(Settings.current_environment)
36
- end
37
-
38
- def config_with_env(config)
39
- env_config = config[Settings.current_environment] || {}
40
- return env_config unless Settings.default_environmental_key?
41
-
42
- default_config = config[Settings.default_environmental_key] || {}
43
- Utils.deep_merge!(default_config, env_config)
44
- end
45
-
46
- def parse_yml(path)
47
- return {} unless File.file?(path)
48
- require "yaml" unless defined?(::YAML)
49
-
50
- # By default, YAML load will return `false` when the yaml document is
51
- # empty. When this occurs, we return an empty hash instead, to match
52
- # the interface when no config file is present.
53
- begin
54
- if defined?(ERB)
55
- ::YAML.load(ERB.new(File.read(path)).result, aliases: true) || {}
56
- else
57
- ::YAML.load_file(path, aliases: true) || {}
58
- end
59
- rescue ArgumentError
60
- if defined?(ERB)
61
- ::YAML.load(ERB.new(File.read(path)).result) || {}
62
- else
63
- ::YAML.load_file(path) || {}
64
- end
65
- end
66
- end
67
-
68
- alias_method :load_base_yml, :parse_yml
69
- alias_method :load_local_yml, :parse_yml
70
-
71
- def local_config_path(path)
72
- path.sub(".yml", ".local.yml")
73
- end
74
-
75
- def relative_config_path(path)
76
- Pathname.new(path).then do |path|
77
- return path if path.relative?
78
- path.relative_path_from(Settings.app_root)
79
- end
80
- end
81
- end
82
- end
83
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anyway
4
- module Loaders
5
- class Registry
6
- attr_reader :registry
7
-
8
- def initialize
9
- @registry = []
10
- end
11
-
12
- def prepend(id, handler = nil, &block)
13
- handler ||= block
14
- insert_at(0, id, handler)
15
- end
16
-
17
- def append(id, handler = nil, &block)
18
- handler ||= block
19
- insert_at(registry.size, id, handler)
20
- end
21
-
22
- def insert_before(another_id, id, handler = nil, &block)
23
- ind = registry.find_index { |(hid, _)| hid == another_id }
24
- raise ArgumentError, "Loader with ID #{another_id} hasn't been registered" if ind.nil?
25
-
26
- handler ||= block
27
- insert_at(ind, id, handler)
28
- end
29
-
30
- def insert_after(another_id, id, handler = nil, &block)
31
- ind = registry.find_index { |(hid, _)| hid == another_id }
32
- raise ArgumentError, "Loader with ID #{another_id} hasn't been registered" if ind.nil?
33
-
34
- handler ||= block
35
- insert_at(ind + 1, id, handler)
36
- end
37
-
38
- def override(id, handler)
39
- find(id).then do |id_to_handler|
40
- raise ArgumentError, "Loader with ID #{id} hasn't been registered" if id_to_handler.nil?
41
- id_to_handler[1] = handler
42
- end
43
- end
44
-
45
- def delete(id)
46
- find(id).then do |id_to_handler|
47
- raise ArgumentError, "Loader with ID #{id} hasn't been registered" if id_to_handler.nil?
48
- registry.delete id_to_handler
49
- end
50
- end
51
-
52
- def each(&block)
53
- registry.each(&block)
54
- end
55
-
56
- def freeze = registry.freeze
57
-
58
- private
59
-
60
- def insert_at(index, id, handler)
61
- raise ArgumentError, "Loader with ID #{id} has been already registered" unless find(id).nil?
62
-
63
- registry.insert(index, [id, handler])
64
- end
65
-
66
- def find(id)
67
- registry.find { |(hid, _)| hid == id }
68
- end
69
- end
70
- end
71
- end
72
-
73
- require "anyway/loaders/base"
74
- require "anyway/loaders/yaml"
75
- require "anyway/loaders/env"
76
- require "anyway/loaders/doppler"
77
- require "anyway/loaders/ejson"
@@ -1,29 +0,0 @@
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
@@ -1,40 +0,0 @@
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
- class << self
8
- attr_reader :tracer, :name_method
9
- attr_accessor :disable_postponed_load_warning
10
-
11
- private
12
-
13
- def tracepoint_class_callback(event)
14
- # Ignore singletons
15
- return if event.self.singleton_class?
16
-
17
- # We wait till `rails/application/configuration.rb` has been loaded, since we rely on it
18
- # See https://github.com/palkan/anyway_config/issues/134
19
- return unless name_method.bind_call(event.self) == "Rails::Application::Configuration"
20
-
21
- tracer.disable
22
-
23
- unless disable_postponed_load_warning
24
- warn "Anyway Config was loaded before Rails. Activating Anyway Config Rails plugins now.\n" \
25
- "NOTE: Already loaded configs were provisioned without Rails-specific sources."
26
- end
27
-
28
- require "anyway/rails"
29
- end
30
- end
31
-
32
- # TruffleRuby doesn't support TracePoint's end event
33
- unless defined?(::TruffleRuby)
34
- @tracer = TracePoint.new(:end, &method(:tracepoint_class_callback))
35
- @tracer.enable
36
- # Use `Module#name` instead of `self.name` to handle overwritten `name` method
37
- @name_method = Module.instance_method(:name)
38
- end
39
- end
40
- end
@@ -1,23 +0,0 @@
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
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anyway
4
- module Rails
5
- module Loaders
6
- class Credentials < Anyway::Loaders::Base
7
- LOCAL_CONTENT_PATH = "config/credentials/local.yml.enc"
8
-
9
- def call(name:, **_options)
10
- return {} unless ::Rails.application.respond_to?(:credentials)
11
-
12
- # do not load from credentials if we're in the context
13
- # of the `credentials:edit` command
14
- return {} if defined?(::Rails::Command::CredentialsCommand)
15
-
16
- # Create a new hash cause credentials are mutable!
17
- config = {}
18
-
19
- trace!(
20
- :credentials,
21
- store: credentials_path
22
- ) do
23
- ::Rails.application.credentials.config[name.to_sym]
24
- end.then do |creds|
25
- Utils.deep_merge!(config, creds) if creds
26
- end
27
-
28
- if use_local?
29
- trace!(:credentials, store: LOCAL_CONTENT_PATH) do
30
- local_credentials(name)
31
- end.then { |creds| Utils.deep_merge!(config, creds) if creds }
32
- end
33
-
34
- config
35
- end
36
-
37
- private
38
-
39
- def local_credentials(name)
40
- local_creds_path = ::Rails.root.join(LOCAL_CONTENT_PATH).to_s
41
-
42
- return unless File.file?(local_creds_path)
43
-
44
- creds = ::Rails.application.encrypted(
45
- local_creds_path,
46
- key_path: ::Rails.root.join("config/credentials/local.key")
47
- )
48
-
49
- creds.config[name.to_sym]
50
- end
51
-
52
- def credentials_path
53
- if ::Rails.application.config.respond_to?(:credentials)
54
- ::Rails.root.join(::Rails.application.config.credentials.content_path).relative_path_from(::Rails.root).to_s
55
- else
56
- "config/credentials.yml.enc"
57
- end
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anyway
4
- module Rails
5
- module Loaders
6
- class Secrets < Anyway::Loaders::Base
7
- def call(name:, **_options)
8
- return {} unless ::Rails.application.respond_to?(:secrets)
9
-
10
- # Create a new hash cause secrets are mutable!
11
- config = {}
12
-
13
- trace!(:secrets) do
14
- secrets.public_send(name)
15
- end.then do |secrets|
16
- Utils.deep_merge!(config, secrets) if secrets
17
- end
18
-
19
- config
20
- end
21
-
22
- private
23
-
24
- def secrets
25
- @secrets ||= ::Rails.application.secrets.tap do |_|
26
- # Reset secrets state if the app hasn't been initialized
27
- # See https://github.com/palkan/anyway_config/issues/14
28
- next if ::Rails.application.initialized?
29
- ::Rails.application.remove_instance_variable(:@secrets)
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anyway
4
- module Rails
5
- module Loaders
6
- class YAML < Anyway::Loaders::YAML; end
7
- end
8
- end
9
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "anyway/rails/loaders/yaml"
4
- require "anyway/rails/loaders/secrets"
5
- require "anyway/rails/loaders/credentials"