qonfig 0.0.0 → 0.12.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/.gitignore +6 -2
- data/.jrubyrc +1 -0
- data/.rspec +1 -1
- data/.rubocop.yml +15 -0
- data/.travis.yml +43 -4
- data/CHANGELOG.md +121 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +1 -1
- data/README.md +1060 -19
- data/Rakefile +18 -4
- data/bin/console +5 -11
- data/bin/rspec +55 -0
- data/bin/setup +1 -0
- data/gemfiles/with_external_deps.gemfile +8 -0
- data/gemfiles/without_external_deps.gemfile +5 -0
- data/lib/qonfig.rb +22 -2
- data/lib/qonfig/command_set.rb +67 -0
- data/lib/qonfig/commands.rb +15 -0
- data/lib/qonfig/commands/add_nested_option.rb +45 -0
- data/lib/qonfig/commands/add_option.rb +41 -0
- data/lib/qonfig/commands/base.rb +12 -0
- data/lib/qonfig/commands/compose.rb +37 -0
- data/lib/qonfig/commands/expose_yaml.rb +159 -0
- data/lib/qonfig/commands/load_from_env.rb +95 -0
- data/lib/qonfig/commands/load_from_env/value_converter.rb +84 -0
- data/lib/qonfig/commands/load_from_json.rb +56 -0
- data/lib/qonfig/commands/load_from_self.rb +73 -0
- data/lib/qonfig/commands/load_from_yaml.rb +58 -0
- data/lib/qonfig/configurable.rb +116 -0
- data/lib/qonfig/data_set.rb +213 -0
- data/lib/qonfig/data_set/class_builder.rb +27 -0
- data/lib/qonfig/data_set/validator.rb +7 -0
- data/lib/qonfig/dsl.rb +122 -0
- data/lib/qonfig/errors.rb +111 -0
- data/lib/qonfig/loaders.rb +9 -0
- data/lib/qonfig/loaders/basic.rb +38 -0
- data/lib/qonfig/loaders/json.rb +24 -0
- data/lib/qonfig/loaders/yaml.rb +24 -0
- data/lib/qonfig/plugins.rb +65 -0
- data/lib/qonfig/plugins/abstract.rb +13 -0
- data/lib/qonfig/plugins/access_mixin.rb +38 -0
- data/lib/qonfig/plugins/registry.rb +125 -0
- data/lib/qonfig/plugins/toml.rb +26 -0
- data/lib/qonfig/plugins/toml/commands/expose_toml.rb +146 -0
- data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +49 -0
- data/lib/qonfig/plugins/toml/data_set.rb +19 -0
- data/lib/qonfig/plugins/toml/dsl.rb +27 -0
- data/lib/qonfig/plugins/toml/loaders/toml.rb +24 -0
- data/lib/qonfig/plugins/toml/tomlrb_fixes.rb +92 -0
- data/lib/qonfig/plugins/toml/uploaders/toml.rb +25 -0
- data/lib/qonfig/settings.rb +457 -0
- data/lib/qonfig/settings/builder.rb +18 -0
- data/lib/qonfig/settings/key_guard.rb +71 -0
- data/lib/qonfig/settings/lock.rb +60 -0
- data/lib/qonfig/uploaders.rb +10 -0
- data/lib/qonfig/uploaders/base.rb +18 -0
- data/lib/qonfig/uploaders/file.rb +55 -0
- data/lib/qonfig/uploaders/json.rb +35 -0
- data/lib/qonfig/uploaders/yaml.rb +93 -0
- data/lib/qonfig/version.rb +7 -1
- data/qonfig.gemspec +29 -17
- metadata +122 -16
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class Qonfig::Loaders::Basic
|
6
|
+
class << self
|
7
|
+
# @param data [String]
|
8
|
+
# @return [void]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.5.0
|
12
|
+
def load(data)
|
13
|
+
nil # NOTE: consciously return nil (for clarity)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [void]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 0.5.0
|
20
|
+
def load_empty_data
|
21
|
+
nil # NOTE: consciously return nil (for clarity)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param file_path [String]
|
25
|
+
# @option fail_on_unexist [Boolean]
|
26
|
+
# @return [Object]
|
27
|
+
#
|
28
|
+
# @raise [Qonfig::FileNotFoundError]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
# @since 0.5.0
|
32
|
+
def load_file(file_path, fail_on_unexist: true)
|
33
|
+
load(::File.read(file_path))
|
34
|
+
rescue Errno::ENOENT => error
|
35
|
+
fail_on_unexist ? (raise Qonfig::FileNotFoundError, error.message) : load_empty_data
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.5.0
|
5
|
+
class Qonfig::Loaders::JSON < Qonfig::Loaders::Basic
|
6
|
+
class << self
|
7
|
+
# @param data [String]
|
8
|
+
# @return [Object]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.5.0
|
12
|
+
def load(data)
|
13
|
+
::JSON.parse(data, max_nesting: false, allow_nan: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Object]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 0.5.0
|
20
|
+
def load_empty_data
|
21
|
+
load('{}')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class Qonfig::Loaders::YAML < Qonfig::Loaders::Basic
|
6
|
+
class << self
|
7
|
+
# @param data [String]
|
8
|
+
# @return [Object]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.2.0
|
12
|
+
def load(data)
|
13
|
+
::YAML.load(ERB.new(data).result)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Object]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 0.5.0
|
20
|
+
def load_empty_data
|
21
|
+
load('{}')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 0.4.0
|
5
|
+
module Qonfig::Plugins
|
6
|
+
require_relative 'plugins/registry'
|
7
|
+
require_relative 'plugins/access_mixin'
|
8
|
+
require_relative 'plugins/abstract'
|
9
|
+
require_relative 'plugins/toml'
|
10
|
+
|
11
|
+
# @since 0.4.0
|
12
|
+
@plugin_registry = Registry.new
|
13
|
+
# @since 0.4.0
|
14
|
+
@access_lock = Mutex.new
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# @param plugin_name [Symbol, String]
|
18
|
+
# @return [void]
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
# @since 0.4.0
|
22
|
+
def load(plugin_name)
|
23
|
+
thread_safe { plugin_registry[plugin_name].load! }
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Array<String>]
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
# @since 0.4.0
|
30
|
+
def names
|
31
|
+
thread_safe { plugin_registry.names }
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param plugin_name [Symbol, String]
|
35
|
+
# @return [void]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.4.0
|
39
|
+
def register_plugin(plugin_name, plugin_module)
|
40
|
+
thread_safe { plugin_registry[plugin_name] = plugin_module }
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [Qonfig::Plugins::Registry]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.4.0
|
49
|
+
attr_reader :plugin_registry
|
50
|
+
|
51
|
+
# @return [Mutex]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
# @since 0.4.0
|
55
|
+
attr_reader :access_lock
|
56
|
+
|
57
|
+
# @return [void]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
# @since 0.4.0
|
61
|
+
def thread_safe
|
62
|
+
access_lock.synchronize { yield if block_given? }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.4.0
|
5
|
+
module Qonfig::Plugins::AccessMixin
|
6
|
+
# @param plugin_name [Symbol, String]
|
7
|
+
# @return [void]
|
8
|
+
#
|
9
|
+
# @see Qonfig::Plugins
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
# @since 0.4.0
|
13
|
+
def plugin(plugin_name)
|
14
|
+
Qonfig::Plugins.load(plugin_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Array<String>]
|
18
|
+
#
|
19
|
+
# @see Qonfig::Plugins
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
# @since 0.4.0
|
23
|
+
def plugins
|
24
|
+
Qonfig::Plugins.names
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param plugin_name [String, Symbol]
|
28
|
+
# @param plugin_klass [Class<Qonfig::Plugins::Abstract>]
|
29
|
+
# @return [void]
|
30
|
+
#
|
31
|
+
# @see Qonfig::Plugins
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
# @since 0.12.0
|
35
|
+
def register_plugin(plugin_name, plugin_klass)
|
36
|
+
Qonfig::Plugins.register_plugin(plugin_name, plugin_klass)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.4.0
|
5
|
+
class Qonfig::Plugins::Registry
|
6
|
+
# @return [void]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.4.0
|
10
|
+
def initialize
|
11
|
+
@plugin_set = {}
|
12
|
+
@access_lock = Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param plugin_name [Symbol, String]
|
16
|
+
# @return [Qonfig::Plugins::Abstract]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 0.4.0
|
20
|
+
def [](plugin_name)
|
21
|
+
thread_safe { fetch(plugin_name) }
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param plugin_name [Symbol, String]
|
25
|
+
# @param plugin_module [Qonfig::Plugins::Abstract]
|
26
|
+
# @return [void]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 0.4.0
|
30
|
+
def register(plugin_name, plugin_module)
|
31
|
+
thread_safe { apply(plugin_name, plugin_module) }
|
32
|
+
end
|
33
|
+
alias_method :[]=, :register
|
34
|
+
|
35
|
+
# @return [Array<String>]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.4.0
|
39
|
+
def names
|
40
|
+
thread_safe { plugin_names }
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [Hash]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.4.0
|
49
|
+
attr_reader :plugin_set
|
50
|
+
|
51
|
+
# @return [Mutex]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
# @since 0.4.0
|
55
|
+
attr_reader :access_lock
|
56
|
+
|
57
|
+
# @return [void]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
# @since 0.4.0
|
61
|
+
def thread_safe
|
62
|
+
access_lock.synchronize { yield if block_given? }
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<String>]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @since 0.4.0
|
69
|
+
def plugin_names
|
70
|
+
plugin_set.keys
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param plugin_name [String]
|
74
|
+
# @return [Boolean]
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
# @since 0.4.0
|
78
|
+
def registered?(plugin_name)
|
79
|
+
plugin_set.key?(plugin_name)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param plugin_name [Symbol, String]
|
83
|
+
# @param plugin_module [Qonfig::Plugins::Abstract]
|
84
|
+
# @return [void]
|
85
|
+
#
|
86
|
+
# @raise [Qonfig::AlreadyRegisteredPluginError]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
# @since 0.4.0
|
90
|
+
def apply(plugin_name, plugin_module)
|
91
|
+
plugin_name = indifferently_accessable_plugin_name(plugin_name)
|
92
|
+
|
93
|
+
if registered?(plugin_name)
|
94
|
+
raise Qonfig::AlreadyRegisteredPluginError, "#{plugin_name} plugin already exist"
|
95
|
+
end
|
96
|
+
|
97
|
+
plugin_set[plugin_name] = plugin_module
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param plugin_name [Symbol, String]
|
101
|
+
# @return [Qonfig::Plugins::Abstract]
|
102
|
+
#
|
103
|
+
# @raise [Qonfig::UnregisteredPluginError]
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
# @since 0.4.0
|
107
|
+
def fetch(plugin_name)
|
108
|
+
plugin_name = indifferently_accessable_plugin_name(plugin_name)
|
109
|
+
|
110
|
+
unless registered?(plugin_name)
|
111
|
+
raise Qonfig::UnregisteredPluginError, "#{plugin_name} plugin is not registered"
|
112
|
+
end
|
113
|
+
|
114
|
+
plugin_set[plugin_name]
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param key [Symbol, String]
|
118
|
+
# @return [String]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
# @since 0.4.0
|
122
|
+
def indifferently_accessable_plugin_name(plugin_name)
|
123
|
+
plugin_name.to_s
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.12.0
|
5
|
+
class Qonfig::Plugins::TOML < Qonfig::Plugins::Abstract
|
6
|
+
class << self
|
7
|
+
# @return [void]
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 0.12.0
|
11
|
+
def load!
|
12
|
+
raise(
|
13
|
+
Qonfig::UnresolvedPluginDependencyError,
|
14
|
+
'::TomlRB does not exist or "toml-rb" gem is not loaded'
|
15
|
+
) unless const_defined?('::TomlRB')
|
16
|
+
|
17
|
+
require_relative 'toml/tomlrb_fixes'
|
18
|
+
require_relative 'toml/loaders/toml'
|
19
|
+
require_relative 'toml/uploaders/toml'
|
20
|
+
require_relative 'toml/commands/load_from_toml'
|
21
|
+
require_relative 'toml/commands/expose_toml'
|
22
|
+
require_relative 'toml/data_set'
|
23
|
+
require_relative 'toml/dsl'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.12.0
|
5
|
+
class Qonfig::Commands::ExposeTOML < Qonfig::Commands::Base
|
6
|
+
# @return [Hash]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.12.0
|
10
|
+
EXPOSERS = { file_name: :file_name, env_key: :env_key }.freeze
|
11
|
+
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.12.0
|
16
|
+
EMPTY_TOML_DATA = {}.freeze
|
17
|
+
|
18
|
+
# @return [String]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.12.0
|
22
|
+
attr_reader :file_path
|
23
|
+
|
24
|
+
# @return [Boolean]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since 0.12.0
|
28
|
+
attr_reader :strict
|
29
|
+
|
30
|
+
# @return [Symbol]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.12.0
|
34
|
+
attr_reader :via
|
35
|
+
|
36
|
+
# @return [Symbol, String]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.12.0
|
40
|
+
attr_reader :env
|
41
|
+
|
42
|
+
# @param file_path [String]
|
43
|
+
# @option strict [Boolean]
|
44
|
+
# @option via [Symbol]
|
45
|
+
# @option env [String, Symbol]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.12.0
|
49
|
+
def initialize(file_path, strict: true, via:, env:)
|
50
|
+
unless env.is_a?(Symbol) || env.is_a?(String) || env.is_a?(Numeric)
|
51
|
+
raise Qonfig::ArgumentError, ':env should be a string or a symbol'
|
52
|
+
end
|
53
|
+
|
54
|
+
raise Qonfig::ArgumentError, ':env should be provided' if env.to_s.empty?
|
55
|
+
raise Qonfig::ArgumentError, 'used :via is unsupported' unless EXPOSERS.key?(via)
|
56
|
+
|
57
|
+
@file_path = file_path
|
58
|
+
@strict = strict
|
59
|
+
@via = via
|
60
|
+
@env = env
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param settings [Qonfig::Settings]
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
# @since 0.12.0
|
68
|
+
def call(settings)
|
69
|
+
case via
|
70
|
+
when EXPOSERS[:file_name]
|
71
|
+
expose_file_name!(settings)
|
72
|
+
when EXPOSERS[:env_key]
|
73
|
+
expose_env_key!(settings)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# @param settings [Qonfig::Settings]
|
80
|
+
# @return [void]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
# @since 0.12.0
|
84
|
+
# rubocop:disable Metrics/AbcSize
|
85
|
+
def expose_file_name!(settings)
|
86
|
+
# NOTE: transform file name (insert environment name into the file name)
|
87
|
+
# from: path/to/file/file_name.file_extension
|
88
|
+
# to: path/to/file/file_name.env_name.file_extension
|
89
|
+
|
90
|
+
pathname = Pathname.new(file_path)
|
91
|
+
dirname = pathname.dirname
|
92
|
+
extname = pathname.extname.to_s
|
93
|
+
basename = pathname.basename.to_s.sub!(extname, '')
|
94
|
+
envname = [env.to_s, extname].reject(&:empty?).join('')
|
95
|
+
envfile = [basename, envname].reject(&:empty?).join('.')
|
96
|
+
realfile = dirname.join(envfile).to_s
|
97
|
+
|
98
|
+
toml_data = load_toml_data(realfile)
|
99
|
+
toml_based_settings = build_data_set_class(toml_data).new.settings
|
100
|
+
|
101
|
+
settings.__append_settings__(toml_based_settings)
|
102
|
+
end
|
103
|
+
# rubocop:enable Metrics/AbcSize
|
104
|
+
|
105
|
+
# @param settings [Qonfig::Settings]
|
106
|
+
# @return [void]
|
107
|
+
#
|
108
|
+
# @raise [Qonfig::ExposeError]
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
# @since 0.12.0
|
112
|
+
# rubocop:disable Metrics/AbcSize
|
113
|
+
def expose_env_key!(settings)
|
114
|
+
toml_data = load_toml_data(file_path)
|
115
|
+
toml_data_slice = toml_data[env] || toml_data[env.to_s] || toml_data[env.to_sym]
|
116
|
+
toml_data_slice = EMPTY_TOML_DATA.dup if toml_data_slice.nil? && !strict
|
117
|
+
|
118
|
+
raise(
|
119
|
+
Qonfig::ExposeError,
|
120
|
+
"#{file_path} file does not contain settings with <#{env}> environment key!"
|
121
|
+
) unless toml_data_slice
|
122
|
+
|
123
|
+
toml_based_settings = build_data_set_class(toml_data_slice).new.settings
|
124
|
+
|
125
|
+
settings.__append_settings__(toml_based_settings)
|
126
|
+
end
|
127
|
+
# rubocop:enable Metrics/AbcSize
|
128
|
+
|
129
|
+
# @param file_path [String]
|
130
|
+
# @return [Hash]
|
131
|
+
#
|
132
|
+
# @api private
|
133
|
+
# @since 0.12.0
|
134
|
+
def load_toml_data(file_path)
|
135
|
+
Qonfig::Loaders::TOML.load_file(file_path, fail_on_unexist: strict)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param toml_data [Hash]
|
139
|
+
# @return [Class<Qonfig::DataSet>]
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
# @since 0.12.0
|
143
|
+
def build_data_set_class(toml_data)
|
144
|
+
Qonfig::DataSet::ClassBuilder.build_from_hash(toml_data)
|
145
|
+
end
|
146
|
+
end
|