runger_config 4.0.0 → 5.0.0

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