qonfig 0.16.0 → 0.17.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 +5 -1
- data/.travis.yml +6 -6
- data/CHANGELOG.md +17 -0
- data/README.md +630 -49
- data/gemfiles/with_external_deps.gemfile +2 -2
- data/lib/qonfig/commands/{add_nested_option.rb → definition/add_nested_option.rb} +1 -1
- data/lib/qonfig/commands/{add_option.rb → definition/add_option.rb} +1 -1
- data/lib/qonfig/commands/{compose.rb → definition/compose.rb} +1 -1
- data/lib/qonfig/commands/{expose_json.rb → definition/expose_json.rb} +1 -1
- data/lib/qonfig/commands/{expose_self.rb → definition/expose_self.rb} +1 -1
- data/lib/qonfig/commands/{expose_yaml.rb → definition/expose_yaml.rb} +1 -1
- data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +82 -0
- data/lib/qonfig/commands/{load_from_env.rb → definition/load_from_env.rb} +1 -1
- data/lib/qonfig/commands/{load_from_json.rb → definition/load_from_json.rb} +1 -1
- data/lib/qonfig/commands/{load_from_self.rb → definition/load_from_self.rb} +1 -1
- data/lib/qonfig/commands/{load_from_yaml.rb → definition/load_from_yaml.rb} +1 -1
- data/lib/qonfig/commands/definition.rb +16 -0
- data/lib/qonfig/commands/instantiation/values_file.rb +171 -0
- data/lib/qonfig/commands/instantiation.rb +7 -0
- data/lib/qonfig/commands.rb +2 -10
- data/lib/qonfig/data_set/lock.rb +61 -4
- data/lib/qonfig/data_set.rb +151 -3
- data/lib/qonfig/dsl.rb +56 -16
- data/lib/qonfig/errors.rb +38 -11
- data/lib/qonfig/loaders/dynamic.rb +52 -0
- data/lib/qonfig/loaders/json.rb +6 -0
- data/lib/qonfig/loaders/yaml.rb +13 -0
- data/lib/qonfig/loaders.rb +3 -0
- data/lib/qonfig/plugins/toml/data_set.rb +13 -0
- data/lib/qonfig/plugins/toml/dsl.rb +6 -2
- data/lib/qonfig/plugins/toml/errors.rb +12 -0
- data/lib/qonfig/plugins/toml/loaders/dynamic.rb +31 -0
- data/lib/qonfig/plugins/toml/loaders/toml.rb +6 -0
- data/lib/qonfig/plugins/toml.rb +2 -0
- data/lib/qonfig/settings/builder.rb +1 -1
- data/lib/qonfig/settings.rb +21 -0
- data/lib/qonfig/validator/basic.rb +9 -1
- data/lib/qonfig/validator/builder/attribute_consistency.rb +29 -0
- data/lib/qonfig/validator/builder.rb +39 -14
- data/lib/qonfig/validator/dsl.rb +9 -1
- data/lib/qonfig/validator/method_based.rb +4 -2
- data/lib/qonfig/validator/predefined/common.rb +4 -2
- data/lib/qonfig/validator/predefined/registry.rb +0 -2
- data/lib/qonfig/validator/predefined/registry_control_mixin.rb +3 -2
- data/lib/qonfig/validator/proc_based.rb +4 -2
- data/lib/qonfig/version.rb +1 -1
- data/qonfig.gemspec +1 -1
- metadata +21 -15
- data/lib/qonfig/commands/load_from_env/value_converter.rb +0 -84
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
module Qonfig::Commands::Definition::LoadFromENV::ValueConverter
|
6
|
+
# @return [Regexp]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.2.0
|
10
|
+
INTEGER_PATTERN = /\A\d+\z/.freeze
|
11
|
+
|
12
|
+
# @return [Regexp]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.2.0
|
16
|
+
FLOAT_PATTERN = /\A\d+\.\d+\z/.freeze
|
17
|
+
|
18
|
+
# @return [Regexp]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.2.0
|
22
|
+
TRUE_PATTERN = /\A(t|true)\z/i.freeze
|
23
|
+
|
24
|
+
# @return [Regexp]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since 0.2.0
|
28
|
+
FALSE_PATTERN = /\A(f|false)\z/i.freeze
|
29
|
+
|
30
|
+
# @return [Regexp]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.2.0
|
34
|
+
ARRAY_PATTERN = /\A[^'"].*\s*,\s*.*[^'"]\z/.freeze
|
35
|
+
|
36
|
+
# @return [Regexp]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.2.0
|
40
|
+
QUOTED_STRING_PATTERN = /\A['"].*['"]\z/.freeze
|
41
|
+
|
42
|
+
class << self
|
43
|
+
# @param env_data [Hash]
|
44
|
+
# @return [void]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.2.0
|
48
|
+
def convert_values!(env_data)
|
49
|
+
env_data.each_pair do |key, value|
|
50
|
+
env_data[key] = convert_value(value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# @param value [Object]
|
57
|
+
# @return [Object]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
# @since 0.2.0
|
61
|
+
def convert_value(value)
|
62
|
+
return value unless value.is_a?(String)
|
63
|
+
|
64
|
+
case value
|
65
|
+
when INTEGER_PATTERN
|
66
|
+
Integer(value)
|
67
|
+
when FLOAT_PATTERN
|
68
|
+
Float(value)
|
69
|
+
when TRUE_PATTERN
|
70
|
+
true
|
71
|
+
when FALSE_PATTERN
|
72
|
+
false
|
73
|
+
when ARRAY_PATTERN
|
74
|
+
value.split(/\s*,\s*/).map(&method(:convert_value))
|
75
|
+
when QUOTED_STRING_PATTERN
|
76
|
+
value.gsub(/(\A['"]|['"]\z)/, '')
|
77
|
+
else
|
78
|
+
value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.17.0
|
5
|
+
module Qonfig::Commands::Definition
|
6
|
+
require_relative 'definition/add_option'
|
7
|
+
require_relative 'definition/add_nested_option'
|
8
|
+
require_relative 'definition/compose'
|
9
|
+
require_relative 'definition/load_from_yaml'
|
10
|
+
require_relative 'definition/load_from_json'
|
11
|
+
require_relative 'definition/load_from_self'
|
12
|
+
require_relative 'definition/load_from_env'
|
13
|
+
require_relative 'definition/expose_yaml'
|
14
|
+
require_relative 'definition/expose_json'
|
15
|
+
require_relative 'definition/expose_self'
|
16
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.17.0
|
5
|
+
class Qonfig::Commands::Instantiation::ValuesFile < Qonfig::Commands::Base
|
6
|
+
# @return [Symbol]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.17.0
|
10
|
+
SELF_LOCATED_FILE_DEFINITION = :self
|
11
|
+
|
12
|
+
# @return [NilClass]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.17.0
|
16
|
+
NO_EXPOSE = nil
|
17
|
+
|
18
|
+
# @return [Boolean]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.17.0
|
22
|
+
DEFAULT_STRICT_BEHAVIOR = false
|
23
|
+
|
24
|
+
# @return [Symbol]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since 0.17.0
|
28
|
+
DEFAULT_FORMAT = :dynamic
|
29
|
+
|
30
|
+
# @return [String, Symbol]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.17.0
|
34
|
+
attr_reader :file_path
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.17.0
|
40
|
+
attr_reader :caller_location
|
41
|
+
|
42
|
+
# @return [String, Symbol]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
# @since 0.17.0
|
46
|
+
attr_reader :format
|
47
|
+
|
48
|
+
# @return [Boolean]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
# @since 0.17.0
|
52
|
+
attr_reader :strict
|
53
|
+
|
54
|
+
# @return [NilClass, String, Symbol]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
# @since 0.17.0
|
58
|
+
attr_reader :expose
|
59
|
+
|
60
|
+
# @param file_path [String, Symbol]
|
61
|
+
# @param caller_location [String]
|
62
|
+
# @option format [String, Symbol]
|
63
|
+
# @option strict [Boolean]
|
64
|
+
# @option expose [NilClass, String, Symbol]
|
65
|
+
# @return [void]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @since 0.17.0
|
69
|
+
def initialize(
|
70
|
+
file_path,
|
71
|
+
caller_location,
|
72
|
+
format: DEFAULT_FORMAT,
|
73
|
+
strict: DEFAULT_STRICT_BEHAVIOR,
|
74
|
+
expose: NO_EXPOSE
|
75
|
+
)
|
76
|
+
prevent_incompatible_attributes!(file_path, format, strict, expose)
|
77
|
+
|
78
|
+
@file_path = file_path
|
79
|
+
@caller_location = caller_location
|
80
|
+
@format = format
|
81
|
+
@strict = strict
|
82
|
+
@expose = expose
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param data_set [Qonfig::DataSet]
|
86
|
+
# @param settings [Qonfig::Settings]
|
87
|
+
# @return [void]
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
# @since 0.17.0
|
91
|
+
def call(data_set, settings)
|
92
|
+
settings_values = load_settings_values
|
93
|
+
return unless settings_values
|
94
|
+
settings_values = (settings_values[expose.to_sym] || settings_values[expose.to_s]) if expose
|
95
|
+
data_set.configure(settings_values) if settings_values
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# @return [Hash]
|
101
|
+
#
|
102
|
+
# @raise [Qonfig::IncompatibleDataStructureError]
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
# @since 0.17.0
|
106
|
+
def load_settings_values
|
107
|
+
(file_path == SELF_LOCATED_FILE_DEFINITION) ? load_from_self : load_from_file
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [Hash]
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
# @since 0.17.0
|
114
|
+
def load_from_file
|
115
|
+
Qonfig::Loaders.resolve(format).load_file(file_path, fail_on_unexist: strict).tap do |values|
|
116
|
+
raise(
|
117
|
+
Qonfig::IncompatibleDataStructureError,
|
118
|
+
'Setting values must be a hash-like structure'
|
119
|
+
) unless values.is_a?(Hash)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# @return [Hash]
|
124
|
+
#
|
125
|
+
# @api private
|
126
|
+
# @since 0.17.0
|
127
|
+
def load_from_self
|
128
|
+
end_data = Qonfig::Loaders::EndData.extract(caller_location)
|
129
|
+
|
130
|
+
Qonfig::Loaders.resolve(format).load(end_data).tap do |values|
|
131
|
+
raise(
|
132
|
+
Qonfig::IncompatibleDataStructureError,
|
133
|
+
'Setting values must be a hash-like structure'
|
134
|
+
) unless values.is_a?(Hash)
|
135
|
+
end
|
136
|
+
rescue Qonfig::SelfDataNotFoundError => error
|
137
|
+
raise(error) if strict
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param file_path [String, Symbol]
|
141
|
+
# @param format [String, Symbol]
|
142
|
+
# @param strict [Boolean]
|
143
|
+
# @param expose [NilClass, String, Symbol]
|
144
|
+
# @return [void]
|
145
|
+
#
|
146
|
+
# @raise [Qonfig::ArgumentError]
|
147
|
+
# @raise [Qonfig::UnsupportedLoaderError]
|
148
|
+
#
|
149
|
+
# @api private
|
150
|
+
# @since 0.17.0
|
151
|
+
def prevent_incompatible_attributes!(file_path, format, strict, expose)
|
152
|
+
unless file_path.is_a?(String) || file_path == SELF_LOCATED_FILE_DEFINITION
|
153
|
+
raise Qonfig::ArgumentError, 'Incorrect file path'
|
154
|
+
end
|
155
|
+
|
156
|
+
unless format.is_a?(String) || format.is_a?(Symbol)
|
157
|
+
raise Qonfig::ArgumentError, 'Format should be a symbol or a string'
|
158
|
+
end
|
159
|
+
|
160
|
+
# NOTE: try to resolve corresponding loader (and fail if cannot be resolved)
|
161
|
+
Qonfig::Loaders.resolve(format)
|
162
|
+
|
163
|
+
unless expose.nil? || expose.is_a?(Symbol) || expose.is_a?(String)
|
164
|
+
raise Qonfig::ArgumentError, ':expose should be a string or a symbol (or nil)'
|
165
|
+
end
|
166
|
+
|
167
|
+
unless strict.is_a?(TrueClass) || strict.is_a?(FalseClass)
|
168
|
+
raise Qonfig::ArgumentError, ':strict should be a type of boolean'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/lib/qonfig/commands.rb
CHANGED
@@ -4,14 +4,6 @@
|
|
4
4
|
# @since 0.1.0
|
5
5
|
module Qonfig::Commands
|
6
6
|
require_relative 'commands/base'
|
7
|
-
require_relative 'commands/
|
8
|
-
require_relative 'commands/
|
9
|
-
require_relative 'commands/compose'
|
10
|
-
require_relative 'commands/load_from_yaml'
|
11
|
-
require_relative 'commands/load_from_json'
|
12
|
-
require_relative 'commands/load_from_self'
|
13
|
-
require_relative 'commands/load_from_env'
|
14
|
-
require_relative 'commands/expose_yaml'
|
15
|
-
require_relative 'commands/expose_json'
|
16
|
-
require_relative 'commands/expose_self'
|
7
|
+
require_relative 'commands/definition'
|
8
|
+
require_relative 'commands/instantiation'
|
17
9
|
end
|
data/lib/qonfig/data_set/lock.rb
CHANGED
@@ -10,24 +10,48 @@ class Qonfig::DataSet::Lock
|
|
10
10
|
def initialize
|
11
11
|
@access_lock = Mutex.new
|
12
12
|
@definition_lock = Mutex.new
|
13
|
+
@arbitary_lock = Mutex.new
|
13
14
|
end
|
14
15
|
|
15
|
-
# @param instructions [
|
16
|
+
# @param instructions [Block]
|
17
|
+
# @return [void]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
# @since 0.17.0
|
21
|
+
def with_arbitary_access(&instructions)
|
22
|
+
acquire_arbitary_lock(&instructions)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param instructions [Block]
|
16
26
|
# @return [void]
|
17
27
|
#
|
18
28
|
# @api private
|
19
29
|
# @since 0.13.0
|
20
30
|
def thread_safe_access(&instructions)
|
21
|
-
|
31
|
+
if arbitary_lock.locked?
|
32
|
+
# :nocov:
|
33
|
+
# NOTE: covered in thread-based specs but simplecov can't gather this fact
|
34
|
+
with_arbitary_access { acquire_access_lock(&instructions) } # :nocov:
|
35
|
+
# :nocov:
|
36
|
+
else
|
37
|
+
acquire_access_lock(&instructions)
|
38
|
+
end
|
22
39
|
end
|
23
40
|
|
24
|
-
# @param instructions [
|
41
|
+
# @param instructions [Block]
|
25
42
|
# @return [void]
|
26
43
|
#
|
27
44
|
# @api private
|
28
45
|
# @since 0.13.0
|
29
46
|
def thread_safe_definition(&instructions)
|
30
|
-
|
47
|
+
if arbitary_lock.locked?
|
48
|
+
# :nocov:
|
49
|
+
# NOTE: covered in thread-based specs but simplecov can't gather this fact
|
50
|
+
with_arbitary_access { acquire_definition_lock(&instructions) }
|
51
|
+
# :nocov:
|
52
|
+
else
|
53
|
+
acquire_definition_lock(&instructions)
|
54
|
+
end
|
31
55
|
end
|
32
56
|
|
33
57
|
private
|
@@ -43,4 +67,37 @@ class Qonfig::DataSet::Lock
|
|
43
67
|
# @api private
|
44
68
|
# @since 0.13.0
|
45
69
|
attr_reader :definition_lock
|
70
|
+
|
71
|
+
# @return [Mutex]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
# @since 0.17.0
|
75
|
+
attr_reader :arbitary_lock
|
76
|
+
|
77
|
+
# @param instructions [Block]
|
78
|
+
# @return [void]
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
# @since 0.17.0
|
82
|
+
def acquire_arbitary_lock(&instructions)
|
83
|
+
arbitary_lock.owned? ? yield : arbitary_lock.synchronize(&instructions)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param instructions [Block]
|
87
|
+
# @return [void]
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
# @since 0.13.0
|
91
|
+
def acquire_access_lock(&instructions)
|
92
|
+
access_lock.owned? ? yield : access_lock.synchronize(&instructions)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param instructions [Block]
|
96
|
+
# @return [void]
|
97
|
+
#
|
98
|
+
# @api private
|
99
|
+
# @since 0.13.0
|
100
|
+
def acquire_definition_lock(&instructions)
|
101
|
+
definition_lock.owned? ? yield : definition_lock.synchronize(&instructions)
|
102
|
+
end
|
46
103
|
end
|