qonfig 0.1.0 → 0.2.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/.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/command_set.rb
CHANGED
@@ -14,6 +14,7 @@ module Qonfig
|
|
14
14
|
# @since 0.1.0
|
15
15
|
def initialize
|
16
16
|
@commands = []
|
17
|
+
@access_lock = Mutex.new
|
17
18
|
end
|
18
19
|
|
19
20
|
# @param command [Qonfig::Commands::Base]
|
@@ -22,7 +23,7 @@ module Qonfig
|
|
22
23
|
# @api private
|
23
24
|
# @since 0.1.0
|
24
25
|
def add_command(command)
|
25
|
-
commands << command
|
26
|
+
thread_safe { commands << command }
|
26
27
|
end
|
27
28
|
alias_method :<<, :add_command
|
28
29
|
|
@@ -32,16 +33,37 @@ module Qonfig
|
|
32
33
|
# @api private
|
33
34
|
# @since 0.1.0
|
34
35
|
def each(&block)
|
35
|
-
block_given? ? commands.each(&block) : commands.each
|
36
|
+
thread_safe { block_given? ? commands.each(&block) : commands.each }
|
36
37
|
end
|
37
38
|
|
38
39
|
# @param command_set [Qonfig::CommandSet]
|
39
|
-
# @return [
|
40
|
+
# @return [void]
|
40
41
|
#
|
41
42
|
# @api private
|
42
43
|
# @since 0.1.0
|
43
44
|
def concat(command_set)
|
44
|
-
commands.concat(command_set.commands)
|
45
|
+
thread_safe { commands.concat(command_set.commands) }
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Qonfig::CommandSet]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
# @since 0.2.0
|
52
|
+
def dup
|
53
|
+
thread_safe do
|
54
|
+
self.class.new.tap { |duplicate| duplicate.concat(self) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# @param block [Proc]
|
61
|
+
# @return [Object]
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
# @since 0.2.0
|
65
|
+
def thread_safe(&block)
|
66
|
+
@access_lock.synchronize(&block)
|
45
67
|
end
|
46
68
|
end
|
47
69
|
end
|
@@ -5,26 +5,33 @@ module Qonfig
|
|
5
5
|
# @api private
|
6
6
|
# @since 0.1.0
|
7
7
|
class AddNestedOption < Base
|
8
|
-
# @return [String
|
8
|
+
# @return [Symbol, String]
|
9
9
|
#
|
10
10
|
# @api private
|
11
11
|
# @since 0.1.0
|
12
12
|
attr_reader :key
|
13
13
|
|
14
|
-
# @return [
|
14
|
+
# @return [Class<Qonfig::DataSet>]
|
15
15
|
#
|
16
16
|
# @api private
|
17
|
-
# @since 0.
|
18
|
-
attr_reader :
|
17
|
+
# @since 0.2.0
|
18
|
+
attr_reader :nested_data_set_klass
|
19
19
|
|
20
|
-
# @param key [String
|
20
|
+
# @param key [Symbol, String]
|
21
21
|
# @param nested_definitions [Proc]
|
22
22
|
#
|
23
|
+
# @raise [Qonfig::ArgumentError]
|
24
|
+
# @raise [Qonfig::CoreMethodIntersectionError]
|
25
|
+
#
|
23
26
|
# @api private
|
24
27
|
# @since 0.1.0
|
25
28
|
def initialize(key, nested_definitions)
|
29
|
+
Qonfig::Settings::KeyGuard.prevent_incomparabilities!(key)
|
30
|
+
|
26
31
|
@key = key
|
27
|
-
@
|
32
|
+
@nested_data_set_klass = Class.new(Qonfig::DataSet).tap do |data_set|
|
33
|
+
data_set.instance_eval(&nested_definitions)
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
37
|
# @param settings [Qonfig::Settings]
|
@@ -33,11 +40,7 @@ module Qonfig
|
|
33
40
|
# @api private
|
34
41
|
# @since 0.1.0
|
35
42
|
def call(settings)
|
36
|
-
|
37
|
-
data_set.instance_eval(&nested_definitions)
|
38
|
-
end
|
39
|
-
|
40
|
-
nested_settings = nested_data_set.new.settings
|
43
|
+
nested_settings = nested_data_set_klass.new.settings
|
41
44
|
|
42
45
|
settings.__define_setting__(key, nested_settings)
|
43
46
|
end
|
@@ -5,7 +5,7 @@ module Qonfig
|
|
5
5
|
# @api private
|
6
6
|
# @since 0.1.0
|
7
7
|
class AddOption < Base
|
8
|
-
# @return [String
|
8
|
+
# @return [Symbol, String]
|
9
9
|
#
|
10
10
|
# @api private
|
11
11
|
# @since 0.1.0
|
@@ -17,13 +17,18 @@ module Qonfig
|
|
17
17
|
# @since 0.1.0
|
18
18
|
attr_reader :value
|
19
19
|
|
20
|
-
# @param key [String
|
20
|
+
# @param key [Symbol, String]
|
21
21
|
# @param value [Object]
|
22
22
|
#
|
23
|
+
# @raise [Qonfig::ArgumentError]
|
24
|
+
# @raise [Qonfig::CoreMethodIntersectionError]
|
25
|
+
#
|
23
26
|
# @api private
|
24
27
|
# @since 0.1.0
|
25
28
|
def initialize(key, value)
|
26
|
-
|
29
|
+
Qonfig::Settings::KeyGuard.prevent_incomparabilities!(key)
|
30
|
+
|
31
|
+
@key = key
|
27
32
|
@value = value
|
28
33
|
end
|
29
34
|
|
@@ -13,9 +13,16 @@ module Qonfig
|
|
13
13
|
|
14
14
|
# @param data_set_klass [Qonfig::DataSet]
|
15
15
|
#
|
16
|
+
# @raise [Qonfig::ArgumentError]
|
17
|
+
#
|
16
18
|
# @api private
|
17
19
|
# @since 0.1.0
|
18
20
|
def initialize(data_set_klass)
|
21
|
+
raise(
|
22
|
+
Qonfig::ArgumentError,
|
23
|
+
'Composed config class should be a subtype of Qonfig::DataSet'
|
24
|
+
) unless data_set_klass.is_a?(Class) && data_set_klass < Qonfig::DataSet
|
25
|
+
|
19
26
|
@data_set_klass = data_set_klass
|
20
27
|
end
|
21
28
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
# @api private
|
5
|
+
# @since 0.2.0
|
6
|
+
module Commands::LoadFromENV::ValueConverter
|
7
|
+
# @return [Regexp]
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 0.2.0
|
11
|
+
INTEGER_PATTERN = /\A\d+\z/
|
12
|
+
|
13
|
+
# @return [Regexp]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
# @since 0.2.0
|
17
|
+
FLOAT_PATTERN = /\A\d+\.\d+\z/
|
18
|
+
|
19
|
+
# @return [Regexp]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
# @since 0.2.0
|
23
|
+
TRUE_PATTERN = /\A(t|true)\z/i
|
24
|
+
|
25
|
+
# @return [Regexp]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.2.0
|
29
|
+
FALSE_PATTERN = /\A(f|false)\z/i
|
30
|
+
|
31
|
+
# @return [Regexp]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
# @since 0.2.0
|
35
|
+
ARRAY_PATTERN = /\A[^'"].*\s*,\s*.*[^'"]\z/
|
36
|
+
|
37
|
+
# @return [Regexp]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
# @since 0.2.0
|
41
|
+
QUOTED_STRING_PATTERN = /\A['"].*['"]\z/
|
42
|
+
|
43
|
+
class << self
|
44
|
+
# @param env_data [Hash]
|
45
|
+
# @return [void]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.2.0
|
49
|
+
def convert_values!(env_data)
|
50
|
+
env_data.each_pair do |key, value|
|
51
|
+
env_data[key] = convert_value(value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# @param value [Object]
|
58
|
+
# @return [Object]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
# @since 0.2.0
|
62
|
+
def convert_value(value)
|
63
|
+
return value unless value.is_a?(String)
|
64
|
+
|
65
|
+
case value
|
66
|
+
when INTEGER_PATTERN
|
67
|
+
Integer(value)
|
68
|
+
when FLOAT_PATTERN
|
69
|
+
Float(value)
|
70
|
+
when TRUE_PATTERN
|
71
|
+
true
|
72
|
+
when FALSE_PATTERN
|
73
|
+
false
|
74
|
+
when ARRAY_PATTERN
|
75
|
+
value.split(/\s*,\s*/).map(&method(:convert_value))
|
76
|
+
when QUOTED_STRING_PATTERN
|
77
|
+
value.gsub(/(\A['"]|['"]\z)/, '')
|
78
|
+
else
|
79
|
+
value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
module Commands
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
class LoadFromENV < Base
|
8
|
+
# @return [Boolean]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.2.0
|
12
|
+
attr_reader :convert_values
|
13
|
+
|
14
|
+
# @return [Regexp]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
# @since 0.2.0
|
18
|
+
attr_reader :prefix_pattern
|
19
|
+
|
20
|
+
# @return [Boolean]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
# @since 0.2.0
|
24
|
+
attr_reader :trim_prefix
|
25
|
+
|
26
|
+
# @return [Regexp]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 0.2.0
|
30
|
+
attr_reader :trim_pattern
|
31
|
+
|
32
|
+
# @option convert_values [Boolean]
|
33
|
+
# @opion prefix [NilClass, String, Regexp]
|
34
|
+
#
|
35
|
+
# @raise [Qonfig::ArgumentError]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.2.0
|
39
|
+
def initialize(convert_values: false, prefix: nil, trim_prefix: false)
|
40
|
+
unless convert_values.is_a?(FalseClass) || convert_values.is_a?(TrueClass)
|
41
|
+
raise Qonfig::ArgumentError, ':convert_values option should be a boolean'
|
42
|
+
end
|
43
|
+
|
44
|
+
unless prefix.is_a?(NilClass) || prefix.is_a?(String) || prefix.is_a?(Regexp)
|
45
|
+
raise Qonfig::ArgumentError, ':prefix option should be a nil / string / regexp'
|
46
|
+
end
|
47
|
+
|
48
|
+
unless trim_prefix.is_a?(FalseClass) || trim_prefix.is_a?(TrueClass)
|
49
|
+
raise Qonfig::ArgumentError, ':trim_refix options should be a boolean'
|
50
|
+
end
|
51
|
+
|
52
|
+
@convert_values = convert_values
|
53
|
+
@prefix_pattern = prefix.is_a?(Regexp) ? prefix : /\A#{Regexp.escape(prefix.to_s)}.*\z/m
|
54
|
+
@trim_prefix = trim_prefix
|
55
|
+
@trim_pattern = prefix.is_a?(Regexp) ? prefix : /\A(#{Regexp.escape(prefix.to_s)})/m
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param settings [Qonfig::Settings]
|
59
|
+
# @return [void]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
# @since 0.2.0
|
63
|
+
def call(settings)
|
64
|
+
env_data = extract_env_data
|
65
|
+
|
66
|
+
env_based_settings = build_data_set_class(env_data).new.settings
|
67
|
+
|
68
|
+
settings.__append_settings__(env_based_settings)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# @return [Hash]
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
# @since 0.2.0
|
77
|
+
def extract_env_data
|
78
|
+
ENV.each_with_object({}) do |(key, value), env_data|
|
79
|
+
next unless key.match(prefix_pattern)
|
80
|
+
key = key.sub(trim_pattern, '') if trim_prefix
|
81
|
+
env_data[key] = value
|
82
|
+
end.tap do |env_data|
|
83
|
+
ValueConverter.convert_values!(env_data) if convert_values
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param env_data [Hash]
|
88
|
+
# @return [Class<Qonfig::DataSet>]
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
# @since 0.2.0
|
92
|
+
def build_data_set_class(env_data)
|
93
|
+
Qonfig::DataSet::ClassBuilder.build_from_hash(env_data)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
module Commands
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
class LoadFromSelf < Base
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.2.0
|
12
|
+
attr_reader :caller_location
|
13
|
+
|
14
|
+
# @param caller_location [String]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
# @since 0.2.0
|
18
|
+
def initialize(caller_location)
|
19
|
+
@caller_location = caller_location
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param settings [Qonfig::Settings]
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
# @since 0.2.0
|
27
|
+
def call(settings)
|
28
|
+
yaml_data = load_self_placed_yaml_data
|
29
|
+
|
30
|
+
yaml_based_settings = build_data_set_klass(yaml_data).new.settings
|
31
|
+
|
32
|
+
settings.__append_settings__(yaml_based_settings)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# @return [Hash]
|
38
|
+
#
|
39
|
+
# @raise [Qonfig::SelfDataNotFound]
|
40
|
+
# @raise [Qonfig::IncompatibleYAMLStructureError]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
# @since 0.2.0
|
44
|
+
def load_self_placed_yaml_data
|
45
|
+
caller_file = caller_location.split(':').first
|
46
|
+
|
47
|
+
raise(
|
48
|
+
Qonfig::SelfDataNotFoundError,
|
49
|
+
"Caller file does not exist! (location: #{caller_location})"
|
50
|
+
) unless File.exist?(caller_file)
|
51
|
+
|
52
|
+
data_match = IO.read(caller_file).match(/\n__END__\n(?<end_data>.*)/m)
|
53
|
+
raise Qonfig::SelfDataNotFoundError, '__END__ data not found!' unless data_match
|
54
|
+
|
55
|
+
end_data = data_match[:end_data]
|
56
|
+
raise Qonfig::SelfDataNotFoundError, '__END__ data not found!' unless end_data
|
57
|
+
|
58
|
+
yaml_data = Qonfig::Loaders::YAML.load(end_data)
|
59
|
+
raise(
|
60
|
+
Qonfig::IncompatibleYAMLStructureError,
|
61
|
+
'YAML content should have a hash-like structure'
|
62
|
+
) unless yaml_data.is_a?(Hash)
|
63
|
+
|
64
|
+
yaml_data
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param self_placed_yaml_data [Hash]
|
68
|
+
# @return [Class<Qonfig::DataSet>]
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
# @since 0.2.0
|
72
|
+
def build_data_set_klass(self_placed_yaml_data)
|
73
|
+
Qonfig::DataSet::ClassBuilder.build_from_hash(self_placed_yaml_data)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
module Commands
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
class LoadFromYAML < Base
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.2.0
|
12
|
+
attr_reader :file_path
|
13
|
+
|
14
|
+
# @return [Boolean]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
# @since 0.2.0
|
18
|
+
attr_reader :strict
|
19
|
+
|
20
|
+
# @param file_path [String]
|
21
|
+
# @option strict [Boolean]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
# @since 0.2.0
|
25
|
+
def initialize(file_path, strict: true)
|
26
|
+
@file_path = file_path
|
27
|
+
@strict = strict
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param settings [Qonfig::Settings]
|
31
|
+
# @return [void]
|
32
|
+
#
|
33
|
+
# @raise [Qonfig::IncompatibleYAMLStructureError]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
# @since 0.2.0
|
37
|
+
def call(settings)
|
38
|
+
yaml_data = Qonfig::Loaders::YAML.load_file(file_path, fail_on_unexist: strict)
|
39
|
+
|
40
|
+
raise(
|
41
|
+
Qonfig::IncompatibleYAMLStructureError,
|
42
|
+
'YAML content should have a hash-like structure'
|
43
|
+
) unless yaml_data.is_a?(Hash)
|
44
|
+
|
45
|
+
yaml_based_settings = build_data_set_class(yaml_data).new.settings
|
46
|
+
|
47
|
+
settings.__append_settings__(yaml_based_settings)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @param yaml_data [Hash]
|
53
|
+
# @return [Class<Qonfig::DataSet>]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
# @since 0.2.0
|
57
|
+
def build_data_set_class(yaml_data)
|
58
|
+
Qonfig::DataSet::ClassBuilder.build_from_hash(yaml_data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
# @api public
|
5
|
+
# @since 0.2.0
|
6
|
+
module Configurable
|
7
|
+
class << self
|
8
|
+
# @param base_klass [Class]
|
9
|
+
# @return [void]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @since 0.2.0
|
13
|
+
def included(base_klass)
|
14
|
+
base_klass.instance_variable_set(:@__qonfig_access_lock__, Mutex.new)
|
15
|
+
base_klass.instance_variable_set(:@__qonfig_definition_lock__, Mutex.new)
|
16
|
+
base_klass.instance_variable_set(:@__qonfig_config_klass__, Class.new(Qonfig::DataSet))
|
17
|
+
base_klass.instance_variable_set(:@__qonfig_config__, nil)
|
18
|
+
|
19
|
+
base_klass.extend(ClassMethods)
|
20
|
+
base_klass.include(InstanceMethods)
|
21
|
+
base_klass.singleton_class.prepend(ClassInheritance)
|
22
|
+
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
# @since 0.2.0
|
29
|
+
module ClassInheritance
|
30
|
+
# @param child_klass [Class]
|
31
|
+
# @return [void]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
# @since 0.2.0
|
35
|
+
def inherited(child_klass)
|
36
|
+
inherited_config_klass = Class.new(@__qonfig_config_klass__)
|
37
|
+
|
38
|
+
child_klass.instance_variable_set(:@__qonfig_definition_lock__, Mutex.new)
|
39
|
+
child_klass.instance_variable_set(:@__qonfig_access_lock__, Mutex.new)
|
40
|
+
child_klass.instance_variable_set(:@__qonfig_config_klass__, inherited_config_klass)
|
41
|
+
child_klass.instance_variable_set(:@__qonfig_config__, nil)
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
# @since 0.2.0
|
49
|
+
module ClassMethods
|
50
|
+
# @param block [Proc]
|
51
|
+
# @return [void]
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
# @since 0.2.0
|
55
|
+
def configuration(&block)
|
56
|
+
@__qonfig_definition_lock__.synchronize do
|
57
|
+
@__qonfig_config_klass__.instance_eval(&block) if block_given?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param block [Proc]
|
62
|
+
# @return [void]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
# @since 0.2.0
|
66
|
+
def configure(&block)
|
67
|
+
@__qonfig_access_lock__.synchronize do
|
68
|
+
config.configure(&block) if block_given?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [Qonfig::DataSet]
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
# @since 0.2.0
|
76
|
+
def config
|
77
|
+
@__qonfig_definition_lock__.synchronize do
|
78
|
+
@__qonfig_config__ ||= @__qonfig_config_klass__.new
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
# @since 0.2.0
|
85
|
+
module InstanceMethods
|
86
|
+
# @return [Qonfig::DataSet]
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
# @since 0.2.0
|
90
|
+
def config
|
91
|
+
self.class.instance_variable_get(:@__qonfig_definition_lock__).synchronize do
|
92
|
+
@__qonfig_config__ ||= self.class.instance_variable_get(:@__qonfig_config_klass__).new
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param block [Proc]
|
97
|
+
# @return [void]
|
98
|
+
#
|
99
|
+
# @api public
|
100
|
+
# @since 0.2.0
|
101
|
+
def configure(&block)
|
102
|
+
self.class.instance_variable_get(:@__qonfig_access_lock__).synchronize do
|
103
|
+
config.configure(&block) if block_given?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
class DataSet
|
5
|
+
# @api private
|
6
|
+
# @since 0.2.0
|
7
|
+
module ClassBuilder
|
8
|
+
class << self
|
9
|
+
# @param hash [Hash]
|
10
|
+
# @return [Class<Qonfig::DataSet>]
|
11
|
+
#
|
12
|
+
# @see Qonfig::DataSet
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.2.0
|
16
|
+
def build_from_hash(hash)
|
17
|
+
Class.new(Qonfig::DataSet).tap do |data_set_klass|
|
18
|
+
hash.each_pair do |key, value|
|
19
|
+
if value.is_a?(Hash)
|
20
|
+
sub_data_set_klass = build_from_hash(value)
|
21
|
+
|
22
|
+
data_set_klass.setting(key) { compose sub_data_set_klass }
|
23
|
+
else
|
24
|
+
data_set_klass.setting key, value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|