runger_config 4.0.0 → 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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/generators/runger/app_config/app_config_generator.rb +6 -10
  4. data/lib/generators/runger/config/config_generator.rb +44 -41
  5. data/lib/generators/runger/install/install_generator.rb +35 -37
  6. data/lib/runger/auto_cast.rb +3 -3
  7. data/lib/runger/config.rb +114 -94
  8. data/lib/runger/dynamic_config.rb +21 -23
  9. data/lib/runger/ejson_parser.rb +24 -24
  10. data/lib/runger/env.rb +50 -52
  11. data/lib/runger/ext/deep_dup.rb +33 -36
  12. data/lib/runger/ext/deep_freeze.rb +28 -32
  13. data/lib/runger/ext/flatten_names.rb +23 -27
  14. data/lib/runger/ext/hash.rb +26 -29
  15. data/lib/runger/ext/string_constantize.rb +12 -15
  16. data/lib/runger/loaders/base.rb +11 -15
  17. data/lib/runger/loaders/doppler.rb +38 -42
  18. data/lib/runger/loaders/ejson.rb +65 -63
  19. data/lib/runger/loaders/env.rb +6 -10
  20. data/lib/runger/loaders/yaml.rb +69 -66
  21. data/lib/runger/loaders.rb +69 -71
  22. data/lib/runger/option_parser_builder.rb +16 -18
  23. data/lib/runger/optparse_config.rb +11 -10
  24. data/lib/runger/rails/autoload.rb +24 -26
  25. data/lib/runger/rails/config.rb +13 -17
  26. data/lib/runger/rails/loaders/credentials.rb +53 -57
  27. data/lib/runger/rails/loaders/secrets.rb +21 -25
  28. data/lib/runger/rails/loaders/yaml.rb +1 -6
  29. data/lib/runger/rails/loaders.rb +3 -3
  30. data/lib/runger/rails/settings.rb +49 -49
  31. data/lib/runger/rails.rb +9 -11
  32. data/lib/runger/railtie.rb +3 -2
  33. data/lib/runger/rbs.rb +29 -29
  34. data/lib/runger/settings.rb +82 -84
  35. data/lib/runger/testing/helpers.rb +26 -28
  36. data/lib/runger/testing.rb +2 -2
  37. data/lib/runger/tracing.rb +143 -136
  38. data/lib/runger/type_casting.rb +16 -11
  39. data/lib/runger/utils/which.rb +10 -12
  40. data/lib/runger/version.rb +1 -1
  41. data/lib/runger.rb +1 -1
  42. data/lib/runger_config.rb +34 -27
  43. metadata +18 -18
@@ -1,61 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "uri"
4
- require "net/http"
5
- require "json"
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
6
 
7
- module Runger
8
- module Loaders
9
- class Doppler < Base
10
- class RequestError < StandardError; end
7
+ class Runger::Loaders::Doppler < Runger::Loaders::Base
8
+ class RequestError < StandardError; end
11
9
 
12
- class << self
13
- attr_accessor :download_url
14
- attr_writer :token
10
+ class << self
11
+ attr_accessor :download_url
12
+ attr_writer :token
15
13
 
16
- def token
17
- @token || ENV["DOPPLER_TOKEN"]
18
- end
19
- end
14
+ def token
15
+ @token || ENV.fetch('DOPPLER_TOKEN', nil)
16
+ end
17
+ end
20
18
 
21
- self.download_url = "https://api.doppler.com/v3/configs/config/secrets/download"
19
+ self.download_url = 'https://api.doppler.com/v3/configs/config/secrets/download'
22
20
 
23
- def call(env_prefix:, **_options)
24
- env_payload = parse_doppler_response(url: Doppler.download_url, token: Doppler.token)
21
+ def call(env_prefix:, **_options)
22
+ env_payload = parse_doppler_response(url: Runger::Loaders::Doppler.download_url, token: Runger::Loaders::Doppler.token)
25
23
 
26
- env = ::Runger::Env.new(type_cast: ::Runger::NoCast, env_container: env_payload)
24
+ env = ::Runger::Env.new(type_cast: ::Runger::NoCast, env_container: env_payload)
27
25
 
28
- env.fetch_with_trace(env_prefix).then do |(conf, trace)|
29
- Tracing.current_trace&.merge!(trace)
30
- conf
31
- end
32
- end
26
+ env.fetch_with_trace(env_prefix).then do |(conf, trace)|
27
+ ::Runger::Tracing.current_trace&.merge!(trace)
28
+ conf
29
+ end
30
+ end
33
31
 
34
- private
32
+ private
35
33
 
36
- def parse_doppler_response(url:, token:)
37
- response = fetch_doppler_config(url, token)
34
+ def parse_doppler_response(url:, token:)
35
+ response = fetch_doppler_config(url, token)
38
36
 
39
- unless response.is_a?(Net::HTTPSuccess)
40
- raise RequestError, "#{response.code} #{response.message}"
41
- end
37
+ unless response.is_a?(Net::HTTPSuccess)
38
+ raise(RequestError, "#{response.code} #{response.message}")
39
+ end
42
40
 
43
- JSON.parse(response.read_body)
44
- end
41
+ JSON.parse(response.read_body)
42
+ end
45
43
 
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?
44
+ def fetch_doppler_config(url, token)
45
+ uri = URI.parse(url)
46
+ raise('Doppler token is required to load configuration from Doppler') if token.nil?
49
47
 
50
- http = Net::HTTP.new(uri.host, uri.port)
51
- http.use_ssl = true if uri.scheme == "https"
48
+ http = Net::HTTP.new(uri.host, uri.port)
49
+ http.use_ssl = true if uri.scheme == 'https'
52
50
 
53
- request = Net::HTTP::Get.new(uri)
54
- request["Accept"] = "application/json"
55
- request["Authorization"] = "Bearer #{token}"
51
+ request = Net::HTTP::Get.new(uri)
52
+ request['Accept'] = 'application/json'
53
+ request['Authorization'] = "Bearer #{token}"
56
54
 
57
- http.request(request)
58
- end
59
- end
55
+ http.request(request)
60
56
  end
61
57
  end
@@ -1,89 +1,91 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "runger/ejson_parser"
3
+ require 'runger/ejson_parser'
4
4
 
5
- module Runger
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: Runger::EJSONParser.new(EJSON.bin_path), **_options)
15
- configs = []
5
+ class Runger::Loaders::EJSON < Runger::Loaders::Base
6
+ class << self
7
+ attr_accessor :bin_path
8
+ end
16
9
 
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
- )
10
+ self.bin_path = 'ejson'
23
11
 
24
- next unless secrets_hash
12
+ def call(
13
+ name:,
14
+ ejson_namespace: name,
15
+ ejson_parser: Runger::EJSONParser.new(Runger::Loaders::EJSON.bin_path),
16
+ **_options
17
+ )
18
+ configs = []
25
19
 
26
- config_hash = if ejson_namespace
27
- secrets_hash[ejson_namespace]
28
- else
29
- secrets_hash.except("_public_key")
30
- end
20
+ rel_config_paths.each do |rel_config_path|
21
+ secrets_hash, rel_path =
22
+ extract_hash_from_rel_config_path(
23
+ ejson_parser:,
24
+ rel_config_path:,
25
+ )
31
26
 
32
- next unless config_hash.is_a?(Hash)
27
+ next unless secrets_hash
33
28
 
34
- configs <<
35
- trace!(:ejson, path: rel_path) do
36
- config_hash
37
- end
29
+ config_hash =
30
+ if ejson_namespace
31
+ secrets_hash[ejson_namespace]
32
+ else
33
+ secrets_hash.except('_public_key')
38
34
  end
39
35
 
40
- return {} if configs.empty?
36
+ next unless config_hash.is_a?(Hash)
41
37
 
42
- configs.inject do |result_config, next_config|
43
- Utils.deep_merge!(result_config, next_config)
38
+ configs <<
39
+ trace!(:ejson, path: rel_path) do
40
+ config_hash
44
41
  end
45
- end
42
+ end
46
43
 
47
- private
44
+ return {} if configs.empty?
48
45
 
49
- def rel_config_paths
50
- chain = [environmental_rel_config_path]
46
+ configs.inject do |result_config, next_config|
47
+ ::Runger::Utils.deep_merge!(result_config, next_config)
48
+ end
49
+ end
51
50
 
52
- chain << "secrets.local.ejson" if use_local?
51
+ private
53
52
 
54
- chain
55
- end
53
+ def rel_config_paths
54
+ chain = [environmental_rel_config_path]
56
55
 
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
56
+ chain << 'secrets.local.ejson' if use_local?
57
+
58
+ chain
59
+ end
68
60
 
69
- def default_rel_config_path
70
- "secrets.ejson"
71
- end
61
+ def environmental_rel_config_path
62
+ if ::Runger::Settings.current_environment
63
+ # if environment file is absent, then take data from the default one
64
+ [
65
+ "#{::Runger::Settings.current_environment}/secrets.ejson",
66
+ default_rel_config_path,
67
+ ]
68
+ else
69
+ default_rel_config_path
70
+ end
71
+ end
72
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)
73
+ def default_rel_config_path
74
+ 'secrets.ejson'
75
+ end
75
76
 
76
- rel_config_path.each do |rel_conf_path|
77
- rel_path = "config/#{rel_conf_path}"
78
- abs_path = "#{Settings.app_root}/#{rel_path}"
77
+ def extract_hash_from_rel_config_path(ejson_parser:, rel_config_path:)
78
+ rel_config_path = Array(rel_config_path)
79
79
 
80
- result = ejson_parser.call(abs_path)
80
+ rel_config_path.each do |rel_conf_path|
81
+ rel_path = "config/#{rel_conf_path}"
82
+ abs_path = "#{::Runger::Settings.app_root}/#{rel_path}"
81
83
 
82
- return [result, rel_path] if result
83
- end
84
+ result = ejson_parser.call(abs_path)
84
85
 
85
- nil
86
- end
86
+ return [result, rel_path] if result
87
87
  end
88
+
89
+ nil
88
90
  end
89
91
  end
@@ -1,16 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Runger
4
- module Loaders
5
- class Env < Base
6
- def call(env_prefix:, **_options)
7
- env = ::Runger::Env.new(type_cast: ::Runger::NoCast)
3
+ class Runger::Loaders::Env < Runger::Loaders::Base
4
+ def call(env_prefix:, **_options)
5
+ env = ::Runger::Env.new(type_cast: ::Runger::NoCast)
8
6
 
9
- env.fetch_with_trace(env_prefix).then do |(conf, trace)|
10
- Tracing.current_trace&.merge!(trace)
11
- conf
12
- end
13
- end
7
+ env.fetch_with_trace(env_prefix).then do |(conf, trace)|
8
+ ::Runger::Tracing.current_trace&.merge!(trace)
9
+ conf
14
10
  end
15
11
  end
16
12
  end
@@ -1,83 +1,86 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pathname"
4
- require "runger/ext/hash"
3
+ require 'pathname'
4
+ require 'runger/ext/hash'
5
5
 
6
6
  using Runger::Ext::Hash
7
7
 
8
- module Runger
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)
8
+ class Runger::Loaders::YAML < Runger::Loaders::Base
9
+ def call(config_path:, **_options)
10
+ rel_config_path = relative_config_path(config_path).to_s
11
+ base_config =
12
+ trace!(:yml, path: rel_config_path) do
13
+ config = load_base_yml(config_path)
14
+ environmental?(config) ? config_with_env(config) : config
23
15
  end
24
16
 
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
17
+ return base_config unless use_local?
37
18
 
38
- def config_with_env(config)
39
- env_config = config[Settings.current_environment] || {}
40
- return env_config unless Settings.default_environmental_key?
19
+ local_path = local_config_path(config_path)
20
+ local_config =
21
+ trace!(:yml, path: relative_config_path(local_path).to_s) {
22
+ load_local_yml(local_path)
23
+ }
24
+ ::Runger::Utils.deep_merge!(base_config, local_config)
25
+ end
41
26
 
42
- default_config = config[Settings.default_environmental_key] || {}
43
- Utils.deep_merge!(default_config, env_config)
44
- end
27
+ private
45
28
 
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
29
+ def environmental?(parsed_yml)
30
+ # strange, but still possible
31
+ return true if ::Runger::Settings.default_environmental_key? && parsed_yml.key?(::Runger::Settings.default_environmental_key)
32
+ # possible
33
+ return true if !::Runger::Settings.future.unwrap_known_environments && ::Runger::Settings.current_environment
34
+ # for other environments
35
+ return true if ::Runger::Settings.known_environments&.any? { parsed_yml.key?(_1) }
67
36
 
68
- alias_method :load_base_yml, :parse_yml
69
- alias_method :load_local_yml, :parse_yml
37
+ # preferred
38
+ parsed_yml.key?(::Runger::Settings.current_environment)
39
+ end
70
40
 
71
- def local_config_path(path)
72
- path.sub(".yml", ".local.yml")
73
- end
41
+ def config_with_env(config)
42
+ env_config = config[::Runger::Settings.current_environment] || {}
43
+ return env_config unless ::Runger::Settings.default_environmental_key?
44
+
45
+ default_config = config[::Runger::Settings.default_environmental_key] || {}
46
+ ::Runger::Utils.deep_merge!(default_config, env_config)
47
+ end
48
+
49
+ def parse_yml(path)
50
+ return {} unless File.file?(path)
74
51
 
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
52
+ require 'yaml' unless defined?(::YAML)
53
+
54
+ # By default, YAML load will return `false` when the yaml document is
55
+ # empty. When this occurs, we return an empty hash instead, to match
56
+ # the interface when no config file is present.
57
+ begin
58
+ if defined?(ERB)
59
+ ::YAML.load(ERB.new(File.read(path)).result, aliases: true) || {}
60
+ else
61
+ ::YAML.load_file(path, aliases: true) || {}
62
+ end
63
+ rescue ArgumentError
64
+ if defined?(ERB)
65
+ ::YAML.load(ERB.new(File.read(path)).result) || {}
66
+ else
67
+ ::YAML.load_file(path) || {}
80
68
  end
81
69
  end
82
70
  end
71
+
72
+ alias load_base_yml parse_yml
73
+ alias load_local_yml parse_yml
74
+
75
+ def local_config_path(path)
76
+ path.sub('.yml', '.local.yml')
77
+ end
78
+
79
+ def relative_config_path(path)
80
+ Pathname.new(path).then do |path|
81
+ return path if path.relative?
82
+
83
+ path.relative_path_from(::Runger::Settings.app_root)
84
+ end
85
+ end
83
86
  end
@@ -1,77 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Runger
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
3
+ class Runger::Loaders::Registry
4
+ attr_reader :registry
5
+
6
+ def initialize
7
+ @registry = []
8
+ end
9
+
10
+ def prepend(id, handler = nil, &block)
11
+ handler ||= block
12
+ insert_at(0, id, handler)
13
+ end
14
+
15
+ def append(id, handler = nil, &block)
16
+ handler ||= block
17
+ insert_at(registry.size, id, handler)
18
+ end
19
+
20
+ def insert_before(another_id, id, handler = nil, &block)
21
+ ind = registry.find_index { |(hid, _)| hid == another_id }
22
+ raise(ArgumentError, "Loader with ID #{another_id} hasn't been registered") if ind.nil?
23
+
24
+ handler ||= block
25
+ insert_at(ind, id, handler)
26
+ end
27
+
28
+ def insert_after(another_id, id, handler = nil, &block)
29
+ ind = registry.find_index { |(hid, _)| hid == another_id }
30
+ raise(ArgumentError, "Loader with ID #{another_id} hasn't been registered") if ind.nil?
31
+
32
+ handler ||= block
33
+ insert_at(ind + 1, id, handler)
34
+ end
35
+
36
+ def override(id, handler)
37
+ find(id).then do |id_to_handler|
38
+ raise(ArgumentError, "Loader with ID #{id} hasn't been registered") if id_to_handler.nil?
39
+
40
+ id_to_handler[1] = handler
69
41
  end
70
42
  end
43
+
44
+ def delete(id)
45
+ find(id).then do |id_to_handler|
46
+ raise(ArgumentError, "Loader with ID #{id} hasn't been registered") if id_to_handler.nil?
47
+
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
71
69
  end
72
70
 
73
- require "runger/loaders/base"
74
- require "runger/loaders/yaml"
75
- require "runger/loaders/env"
76
- require "runger/loaders/doppler"
77
- require "runger/loaders/ejson"
71
+ require 'runger/loaders/base'
72
+ require 'runger/loaders/doppler'
73
+ require 'runger/loaders/ejson'
74
+ require 'runger/loaders/env'
75
+ require 'runger/loaders/yaml'
@@ -1,29 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "optparse"
3
+ require 'optparse'
4
4
 
5
- module Runger # :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
5
+ # Initializes the OptionParser instance using the given configuration
6
+ class Runger::OptionParserBuilder
7
+ class << self
8
+ def call(options)
9
+ OptionParser.new do |opts|
10
+ options.each do |key, descriptor|
11
+ opts.on(*option_parser_on_args(key, **descriptor)) do |val|
12
+ yield([key, val])
15
13
  end
16
14
  end
17
15
  end
16
+ end
18
17
 
19
- private
18
+ private
20
19
 
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
20
+ def option_parser_on_args(key, flag: false, desc: nil, type: ::String)
21
+ on_args = ["--#{key.to_s.tr('_', '-')}#{flag ? '' : ' VALUE'}"]
22
+ on_args << type unless flag
23
+ on_args << desc unless desc.nil?
24
+ on_args
27
25
  end
28
26
  end
29
27
  end