runger_config 3.0.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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"