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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/generators/runger/app_config/app_config_generator.rb +6 -10
- data/lib/generators/runger/config/config_generator.rb +44 -41
- data/lib/generators/runger/install/install_generator.rb +35 -37
- data/lib/runger/auto_cast.rb +3 -3
- data/lib/runger/config.rb +114 -94
- data/lib/runger/dynamic_config.rb +21 -23
- data/lib/runger/ejson_parser.rb +24 -24
- data/lib/runger/env.rb +50 -52
- data/lib/runger/ext/deep_dup.rb +33 -36
- data/lib/runger/ext/deep_freeze.rb +28 -32
- data/lib/runger/ext/flatten_names.rb +23 -27
- data/lib/runger/ext/hash.rb +26 -29
- data/lib/runger/ext/string_constantize.rb +12 -15
- data/lib/runger/loaders/base.rb +11 -15
- data/lib/runger/loaders/doppler.rb +38 -42
- data/lib/runger/loaders/ejson.rb +65 -63
- data/lib/runger/loaders/env.rb +6 -10
- data/lib/runger/loaders/yaml.rb +69 -66
- data/lib/runger/loaders.rb +69 -71
- data/lib/runger/option_parser_builder.rb +16 -18
- data/lib/runger/optparse_config.rb +11 -10
- data/lib/runger/rails/autoload.rb +24 -26
- data/lib/runger/rails/config.rb +13 -17
- data/lib/runger/rails/loaders/credentials.rb +53 -57
- data/lib/runger/rails/loaders/secrets.rb +21 -25
- data/lib/runger/rails/loaders/yaml.rb +1 -6
- data/lib/runger/rails/loaders.rb +3 -3
- data/lib/runger/rails/settings.rb +49 -49
- data/lib/runger/rails.rb +9 -11
- data/lib/runger/railtie.rb +3 -2
- data/lib/runger/rbs.rb +29 -29
- data/lib/runger/settings.rb +82 -84
- data/lib/runger/testing/helpers.rb +26 -28
- data/lib/runger/testing.rb +2 -2
- data/lib/runger/tracing.rb +143 -136
- data/lib/runger/type_casting.rb +16 -11
- data/lib/runger/utils/which.rb +10 -12
- data/lib/runger/version.rb +1 -1
- data/lib/runger.rb +1 -1
- data/lib/runger_config.rb +34 -27
- metadata +18 -18
@@ -1,61 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
6
|
|
7
|
-
|
8
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
10
|
+
class << self
|
11
|
+
attr_accessor :download_url
|
12
|
+
attr_writer :token
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def token
|
15
|
+
@token || ENV.fetch('DOPPLER_TOKEN', nil)
|
16
|
+
end
|
17
|
+
end
|
20
18
|
|
21
|
-
|
19
|
+
self.download_url = 'https://api.doppler.com/v3/configs/config/secrets/download'
|
22
20
|
|
23
|
-
|
24
|
-
|
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
|
-
|
24
|
+
env = ::Runger::Env.new(type_cast: ::Runger::NoCast, env_container: env_payload)
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
32
|
+
private
|
35
33
|
|
36
|
-
|
37
|
-
|
34
|
+
def parse_doppler_response(url:, token:)
|
35
|
+
response = fetch_doppler_config(url, token)
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
unless response.is_a?(Net::HTTPSuccess)
|
38
|
+
raise(RequestError, "#{response.code} #{response.message}")
|
39
|
+
end
|
42
40
|
|
43
|
-
|
44
|
-
|
41
|
+
JSON.parse(response.read_body)
|
42
|
+
end
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
51
|
-
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
http.use_ssl = true if uri.scheme == 'https'
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
request = Net::HTTP::Get.new(uri)
|
52
|
+
request['Accept'] = 'application/json'
|
53
|
+
request['Authorization'] = "Bearer #{token}"
|
56
54
|
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
55
|
+
http.request(request)
|
60
56
|
end
|
61
57
|
end
|
data/lib/runger/loaders/ejson.rb
CHANGED
@@ -1,89 +1,91 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'runger/ejson_parser'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
27
|
+
next unless secrets_hash
|
33
28
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
36
|
+
next unless config_hash.is_a?(Hash)
|
41
37
|
|
42
|
-
|
43
|
-
|
38
|
+
configs <<
|
39
|
+
trace!(:ejson, path: rel_path) do
|
40
|
+
config_hash
|
44
41
|
end
|
45
|
-
|
42
|
+
end
|
46
43
|
|
47
|
-
|
44
|
+
return {} if configs.empty?
|
48
45
|
|
49
|
-
|
50
|
-
|
46
|
+
configs.inject do |result_config, next_config|
|
47
|
+
::Runger::Utils.deep_merge!(result_config, next_config)
|
48
|
+
end
|
49
|
+
end
|
51
50
|
|
52
|
-
|
51
|
+
private
|
53
52
|
|
54
|
-
|
55
|
-
|
53
|
+
def rel_config_paths
|
54
|
+
chain = [environmental_rel_config_path]
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
74
|
-
|
73
|
+
def default_rel_config_path
|
74
|
+
'secrets.ejson'
|
75
|
+
end
|
75
76
|
|
76
|
-
|
77
|
-
|
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
|
-
|
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
|
-
|
83
|
-
end
|
84
|
+
result = ejson_parser.call(abs_path)
|
84
85
|
|
85
|
-
|
86
|
-
end
|
86
|
+
return [result, rel_path] if result
|
87
87
|
end
|
88
|
+
|
89
|
+
nil
|
88
90
|
end
|
89
91
|
end
|
data/lib/runger/loaders/env.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
data/lib/runger/loaders/yaml.rb
CHANGED
@@ -1,83 +1,86 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'pathname'
|
4
|
+
require 'runger/ext/hash'
|
5
5
|
|
6
6
|
using Runger::Ext::Hash
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
43
|
-
Utils.deep_merge!(default_config, env_config)
|
44
|
-
end
|
27
|
+
private
|
45
28
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
69
|
-
|
37
|
+
# preferred
|
38
|
+
parsed_yml.key?(::Runger::Settings.current_environment)
|
39
|
+
end
|
70
40
|
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
data/lib/runger/loaders.rb
CHANGED
@@ -1,77 +1,75 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
74
|
-
require
|
75
|
-
require
|
76
|
-
require
|
77
|
-
require
|
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
|
3
|
+
require 'optparse'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
class
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
18
|
+
private
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|