qonfig 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -3
- data/CHANGELOG.md +47 -1
- data/README.md +472 -19
- data/lib/qonfig/command_set.rb +26 -4
- data/lib/qonfig/commands/add_nested_option.rb +14 -11
- data/lib/qonfig/commands/add_option.rb +8 -3
- data/lib/qonfig/commands/compose.rb +7 -0
- data/lib/qonfig/commands/load_from_env/value_converter.rb +84 -0
- data/lib/qonfig/commands/load_from_env.rb +97 -0
- data/lib/qonfig/commands/load_from_self.rb +77 -0
- data/lib/qonfig/commands/load_from_yaml.rb +62 -0
- data/lib/qonfig/configurable.rb +108 -0
- data/lib/qonfig/data_set/class_builder.rb +32 -0
- data/lib/qonfig/data_set.rb +96 -5
- data/lib/qonfig/dsl.rb +48 -7
- data/lib/qonfig/error.rb +47 -1
- data/lib/qonfig/loaders/yaml.rb +34 -0
- data/lib/qonfig/settings/builder.rb +22 -0
- data/lib/qonfig/settings/key_guard.rb +75 -0
- data/lib/qonfig/settings/lock.rb +64 -0
- data/lib/qonfig/settings.rb +215 -54
- data/lib/qonfig/version.rb +2 -2
- data/lib/qonfig.rb +13 -1
- data/qonfig.gemspec +3 -3
- metadata +18 -9
- data/lib/qonfig/settings_builder.rb +0 -20
data/lib/qonfig/data_set.rb
CHANGED
@@ -13,10 +13,15 @@ module Qonfig
|
|
13
13
|
# @since 0.1.0
|
14
14
|
attr_reader :settings
|
15
15
|
|
16
|
+
# @param configurations [Proc]
|
17
|
+
#
|
16
18
|
# @api public
|
17
19
|
# @since 0.1.0
|
18
|
-
def initialize
|
19
|
-
@
|
20
|
+
def initialize(&configurations)
|
21
|
+
@__access_lock__ = Mutex.new
|
22
|
+
@__definition_lock__ = Mutex.new
|
23
|
+
|
24
|
+
thread_safe_definition { load!(&configurations) }
|
20
25
|
end
|
21
26
|
|
22
27
|
# @return [void]
|
@@ -24,7 +29,29 @@ module Qonfig
|
|
24
29
|
# @api public
|
25
30
|
# @since 0.1.0
|
26
31
|
def freeze!
|
27
|
-
settings.__freeze__
|
32
|
+
thread_safe_access { settings.__freeze__ }
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [void]
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
# @since 0.2.0
|
39
|
+
def frozen?
|
40
|
+
thread_safe_access { settings.__is_frozen__ }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param configurations [Proc]
|
44
|
+
# @return [void]
|
45
|
+
#
|
46
|
+
# @raise [Qonfig::FrozenSettingsError]
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
# @since 0.2.0
|
50
|
+
def reload!(&configurations)
|
51
|
+
thread_safe_definition do
|
52
|
+
raise Qonfig::FrozenSettingsError, 'Frozen config can not be reloaded' if frozen?
|
53
|
+
load!(&configurations)
|
54
|
+
end
|
28
55
|
end
|
29
56
|
|
30
57
|
# @return [void]
|
@@ -32,7 +59,7 @@ module Qonfig
|
|
32
59
|
# @api public
|
33
60
|
# @since 0.1.0
|
34
61
|
def configure
|
35
|
-
yield(settings) if block_given?
|
62
|
+
thread_safe_access { yield(settings) if block_given? }
|
36
63
|
end
|
37
64
|
|
38
65
|
# @return [Hash]
|
@@ -40,8 +67,72 @@ module Qonfig
|
|
40
67
|
# @api public
|
41
68
|
# @since 0.1.0
|
42
69
|
def to_h
|
43
|
-
settings.__to_hash__
|
70
|
+
thread_safe_access { settings.__to_hash__ }
|
44
71
|
end
|
45
72
|
alias_method :to_hash, :to_h
|
73
|
+
|
74
|
+
# @param key [String, Symbol]
|
75
|
+
# @return [Object]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
# @since 0.2.0
|
79
|
+
def [](key)
|
80
|
+
thread_safe_access { settings[key] }
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param keys [Array<String, Symbol>]
|
84
|
+
# @return [Object]
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
# @since 0.2.0
|
88
|
+
def dig(*keys)
|
89
|
+
thread_safe_access { settings.__dig__(*keys) }
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [void]
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
# @since 0.2.0
|
96
|
+
def clear!
|
97
|
+
thread_safe_access { settings.__clear__ }
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# @return [Qonfig::Settings]
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
# @since 0.2.0
|
106
|
+
def build_settings
|
107
|
+
Qonfig::Settings::Builder.build(self.class.commands.dup)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param configurations [Proc]
|
111
|
+
# @return [void]
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
# @since 0.2.0
|
115
|
+
def load!(&configurations)
|
116
|
+
@settings = build_settings
|
117
|
+
configure(&configurations) if block_given?
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param instructions [Proc]
|
121
|
+
# @return [Object]
|
122
|
+
#
|
123
|
+
# @api private
|
124
|
+
# @since 0.2.0
|
125
|
+
def thread_safe_access(&instructions)
|
126
|
+
@__access_lock__.synchronize(&instructions)
|
127
|
+
end
|
128
|
+
|
129
|
+
# @param instructions [Proc]
|
130
|
+
# @return [Object]
|
131
|
+
#
|
132
|
+
# @api private
|
133
|
+
# @since 0.2.0
|
134
|
+
def thread_safe_definition(&instructions)
|
135
|
+
@__definition_lock__.synchronize(&instructions)
|
136
|
+
end
|
46
137
|
end
|
47
138
|
end
|
data/lib/qonfig/dsl.rb
CHANGED
@@ -13,12 +13,13 @@ module Qonfig
|
|
13
13
|
def extended(child_klass)
|
14
14
|
child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
|
15
15
|
|
16
|
-
|
16
|
+
child_klass.singleton_class.prepend(Module.new do
|
17
17
|
def inherited(child_klass)
|
18
18
|
child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
|
19
19
|
child_klass.commands.concat(commands)
|
20
|
+
super
|
20
21
|
end
|
21
|
-
end
|
22
|
+
end)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
@@ -30,18 +31,17 @@ module Qonfig
|
|
30
31
|
@commands
|
31
32
|
end
|
32
33
|
|
33
|
-
# @param key [String
|
34
|
+
# @param key [Symbol, String]
|
34
35
|
# @param initial_value [Object]
|
35
36
|
# @param nested_settings [Proc]
|
36
37
|
# @return [void]
|
37
38
|
#
|
39
|
+
# @see Qonfig::Commands::AddNestedOption
|
40
|
+
# @see Qonfig::Commands::AddOption
|
41
|
+
#
|
38
42
|
# @api public
|
39
43
|
# @since 0.1.0
|
40
44
|
def setting(key, initial_value = nil, &nested_settings)
|
41
|
-
unless key.is_a?(Symbol) || key.is_a?(String)
|
42
|
-
raise Qonfig::ArgumentError, 'Setting key should be a symbol or a string!'
|
43
|
-
end
|
44
|
-
|
45
45
|
if block_given?
|
46
46
|
commands << Qonfig::Commands::AddNestedOption.new(key, nested_settings)
|
47
47
|
else
|
@@ -52,10 +52,51 @@ module Qonfig
|
|
52
52
|
# @param data_set_klass [Class{Qonfig::DataSet}]
|
53
53
|
# @return [void]
|
54
54
|
#
|
55
|
+
# @see Qonfig::Comamnds::Compose
|
56
|
+
#
|
55
57
|
# @api private
|
56
58
|
# @sine 0.1.0
|
57
59
|
def compose(data_set_klass)
|
58
60
|
commands << Qonfig::Commands::Compose.new(data_set_klass)
|
59
61
|
end
|
62
|
+
|
63
|
+
# @param file_path [String]
|
64
|
+
# @option strict [Boolean]
|
65
|
+
# @return [void]
|
66
|
+
#
|
67
|
+
# @see Qonfig::Commands::LoadFromYAML
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
# @since 0.2.0
|
71
|
+
def load_from_yaml(file_path, strict: true)
|
72
|
+
commands << Qonfig::Commands::LoadFromYAML.new(file_path, strict: strict)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [void]
|
76
|
+
#
|
77
|
+
# @see Qonfig::Commands::LoadFromSelf
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
# @since 0.2.0
|
81
|
+
def load_from_self
|
82
|
+
caller_location = caller(1, 1).first
|
83
|
+
commands << Qonfig::Commands::LoadFromSelf.new(caller_location)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @option convert_values [Boolean]
|
87
|
+
# @option prefix [NilClass, String, Regexp]
|
88
|
+
# @return [void]
|
89
|
+
#
|
90
|
+
# @see Qonfig::Commands::LoadFromENV
|
91
|
+
#
|
92
|
+
# @api public
|
93
|
+
# @since 0.2.0
|
94
|
+
def load_from_env(convert_values: false, prefix: nil, trim_prefix: false)
|
95
|
+
commands << Qonfig::Commands::LoadFromENV.new(
|
96
|
+
convert_values: convert_values,
|
97
|
+
prefix: prefix,
|
98
|
+
trim_prefix: trim_prefix
|
99
|
+
)
|
100
|
+
end
|
60
101
|
end
|
61
102
|
end
|
data/lib/qonfig/error.rb
CHANGED
@@ -9,11 +9,57 @@ module Qonfig
|
|
9
9
|
# @since 0.1.0
|
10
10
|
ArgumentError = Class.new(Error)
|
11
11
|
|
12
|
+
# @see Qonfig::Settings
|
13
|
+
#
|
12
14
|
# @api public
|
13
15
|
# @since 0.1.0
|
14
16
|
UnknownSettingError = Class.new(Error)
|
15
17
|
|
18
|
+
# @see Qonfig::Settings
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
# @since 0.2.0
|
22
|
+
AmbiguousSettingValueError = Class.new(Error)
|
23
|
+
|
24
|
+
# @see Qonfig::Settings
|
25
|
+
# @see Qonfig::Settings::KeyGuard
|
26
|
+
# @see Qonfig::Commands::AddOption
|
27
|
+
# @see Qonfig::Commands::AddNestedOption
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
# @since 0.2.0
|
31
|
+
CoreMethodIntersectionError = Class.new(Error)
|
32
|
+
|
33
|
+
# @see Qonfig::Settings
|
34
|
+
# @see Qonfig::DataSet
|
35
|
+
#
|
16
36
|
# @api public
|
17
37
|
# @since 0.1.0
|
18
|
-
FrozenSettingsError =
|
38
|
+
FrozenSettingsError = begin # rubocop:disable Naming/ConstantName
|
39
|
+
# :nocov:
|
40
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
41
|
+
Class.new(::FrozenError)
|
42
|
+
else
|
43
|
+
Class.new(::RuntimeError)
|
44
|
+
end
|
45
|
+
# :nocov:
|
46
|
+
end
|
47
|
+
|
48
|
+
# @see Qonfig::Commands::LoadFromYAML
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
# @since 0.2.0
|
52
|
+
IncompatibleYAMLStructureError = Class.new(Error)
|
53
|
+
|
54
|
+
# @see Qonfig::Loaders::YAML
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
# @since 0.2.0
|
58
|
+
FileNotFoundError = Class.new(Errno::ENOENT)
|
59
|
+
|
60
|
+
# @see Qonfig::Commands::LoadFromSelf
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
# @since 0.2.0
|
64
|
+
SelfDataNotFoundError = Class.new(Error)
|
19
65
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
module Loaders
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
module YAML
|
8
|
+
class << self
|
9
|
+
# @param data [String]
|
10
|
+
# @return [Object]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 0.2.0
|
14
|
+
def load(data)
|
15
|
+
::YAML.load(ERB.new(data).result)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param file_path [String]
|
19
|
+
# @option fail_on_unexist [Boolean]
|
20
|
+
# @return [Object]
|
21
|
+
#
|
22
|
+
# @raise [Qonfig::FileNotFoundError]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
# @since 0.2.0
|
26
|
+
def load_file(file_path, fail_on_unexist: true)
|
27
|
+
load(::File.read(file_path))
|
28
|
+
rescue Errno::ENOENT => error
|
29
|
+
fail_on_unexist ? (raise Qonfig::FileNotFoundError, error.message) : load('{}')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
class Settings
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
module Builder
|
8
|
+
class << self
|
9
|
+
# @param [Qonfig::CommandSet]
|
10
|
+
# @return [Qonfig::Settings]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 0.2.0
|
14
|
+
def build(commands)
|
15
|
+
Qonfig::Settings.new.tap do |settings|
|
16
|
+
commands.each { |command| command.call(settings) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
class Settings
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
class KeyGuard
|
8
|
+
class << self
|
9
|
+
# @param key [String, Symbol, Object]
|
10
|
+
# @return [void]
|
11
|
+
#
|
12
|
+
# @raise [Qonfig::ArgumentError]
|
13
|
+
# @raise [Qonfig::CoreMethodIntersectionError]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
# @since 0.2.0
|
17
|
+
def prevent_incomparabilities!(key)
|
18
|
+
new(key).prevent_incomparabilities!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String, Symbol, Object]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
# @sicne 0.2.0
|
26
|
+
attr_reader :key
|
27
|
+
|
28
|
+
# @param key [String, Symbol, Object]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
# @since 0.2.0
|
32
|
+
def initialize(key)
|
33
|
+
@key = key
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [void]
|
37
|
+
#
|
38
|
+
# @raise [Qonfig::ArgumentError]
|
39
|
+
# @raise [Qonfig::CoreMethodIntersectionError]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
# @since 0.2.0
|
43
|
+
def prevent_incomparabilities!
|
44
|
+
prevent_incompatible_key_type!
|
45
|
+
prevent_core_method_intersection!
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [void]
|
49
|
+
#
|
50
|
+
# @raise [Qonfig::ArgumentError]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
# @since 0.2.0
|
54
|
+
def prevent_incompatible_key_type!
|
55
|
+
raise(
|
56
|
+
Qonfig::ArgumentError,
|
57
|
+
'Setting key should be a symbol or a string!'
|
58
|
+
) unless key.is_a?(Symbol) || key.is_a?(String)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [void]
|
62
|
+
#
|
63
|
+
# @raise [Qonfig::CoreMethodIntersectionError]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
# @since 0.2.0
|
67
|
+
def prevent_core_method_intersection!
|
68
|
+
raise(
|
69
|
+
Qonfig::CoreMethodIntersectionError,
|
70
|
+
"<#{key}> key can not be used since this is a private core method"
|
71
|
+
) if Qonfig::Settings::CORE_METHODS.include?(key.to_s)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
class Settings
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
class Lock
|
8
|
+
# @api private
|
9
|
+
# @since 0.2.0
|
10
|
+
def initialize
|
11
|
+
@definition_lock = Mutex.new
|
12
|
+
@access_lock = Mutex.new
|
13
|
+
@merge_lock = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param instructions [Proc]
|
17
|
+
# @return [Object]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
# @since 0.2.0
|
21
|
+
def thread_safe_definition(&instructions)
|
22
|
+
definition_lock.synchronize(&instructions)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param instructions [Proc]
|
26
|
+
# @return [Object]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 0.2.0
|
30
|
+
def thread_safe_access(&instructions)
|
31
|
+
access_lock.synchronize(&instructions)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param instructions [Proc]
|
35
|
+
# @return [Object]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.2.0
|
39
|
+
def thread_safe_merge(&instructions)
|
40
|
+
merge_lock.synchronize(&instructions)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [Mutex]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.2.0
|
49
|
+
attr_reader :definition_lock
|
50
|
+
|
51
|
+
# @return [Mutex]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
# @since 0.2.0
|
55
|
+
attr_reader :access_lock
|
56
|
+
|
57
|
+
# @return [Mutex]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
# @since 0.2.0
|
61
|
+
attr_reader :merge_lock
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|