mixlib-config 2.2.4 → 2.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +0 -2
- data/README.md +151 -3
- data/Rakefile +11 -15
- data/lib/mixlib/config.rb +243 -17
- data/lib/mixlib/config/version.rb +1 -1
- data/mixlib-config.gemspec +4 -2
- data/spec/mixlib/config_spec.rb +219 -8
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '05795379d8df703468f3f0dea7e8e28f9229cbca557e1e6ab6b1bf5b36781536'
|
4
|
+
data.tar.gz: 951f9d4f144a3d60a46d8c702962c7c9ca08eafbe6a3edd104b32b347691ebb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eeb6834b5842c7b4ac030f4465a9d54e784cd8925cfc09fb0d307b35241eedd4769abf7eb67ded947093180d53c35491f8b5444e479d75604434aa385df62277
|
7
|
+
data.tar.gz: 76e29d1167cfcc8314c1823971c7290fc4da7f403f06a99f42480e9adc1390b70482a192a08a619f11f61568c207a43feec9591e2a97890cf27b645454942505
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -43,6 +43,32 @@ And you can modify configuration values with this syntax:
|
|
43
43
|
MyConfig[:first_value] = 'foobar' # sets first_value to 'foobar'
|
44
44
|
```
|
45
45
|
|
46
|
+
If you prefer to allow your users to pass in configuration via YAML or JSON files, `mixlib-config` supports that too!
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
MyConfig.from_file('~/.myconfig.yml')
|
50
|
+
MyConfig.from_file('~/.myconfig.json')
|
51
|
+
```
|
52
|
+
|
53
|
+
This way, a user could write a YAML config file that looked like this:
|
54
|
+
|
55
|
+
```yaml
|
56
|
+
---
|
57
|
+
first_value: 'hi'
|
58
|
+
second_value: 'goodbye'
|
59
|
+
```
|
60
|
+
|
61
|
+
or a JSON file that looks like this:
|
62
|
+
|
63
|
+
```json
|
64
|
+
{
|
65
|
+
"first_value": "hi",
|
66
|
+
"second_value": "goodbye"
|
67
|
+
}
|
68
|
+
```
|
69
|
+
|
70
|
+
Please note: There is an inherent limitation in the logic you can do with YAML and JSON file. At this time, `mixlib-config` does not support ERB or other logic in YAML or JSON config (read "static content only").
|
71
|
+
|
46
72
|
## Nested Configuration
|
47
73
|
|
48
74
|
Often you want to be able to group configuration options to provide a common context. Mixlib::Config supports this thus:
|
@@ -61,14 +87,17 @@ Often you want to be able to group configuration options to provide a common con
|
|
61
87
|
|
62
88
|
The user can write their config file in one of three formats:
|
63
89
|
|
64
|
-
|
90
|
+
### Method Style
|
91
|
+
|
65
92
|
```ruby
|
66
93
|
logging.base_filename 'superlog'
|
67
94
|
logging.max_log_files 2
|
68
95
|
```
|
69
96
|
|
70
|
-
|
97
|
+
### Block Style
|
98
|
+
|
71
99
|
Using this format the block is executed in the context, so all configurables on that context is directly accessible
|
100
|
+
|
72
101
|
```ruby
|
73
102
|
logging do
|
74
103
|
base_filename 'superlog'
|
@@ -76,8 +105,10 @@ logging do
|
|
76
105
|
end
|
77
106
|
```
|
78
107
|
|
79
|
-
|
108
|
+
### Block with Argument Style
|
109
|
+
|
80
110
|
Using this format the context is given to the block as an argument
|
111
|
+
|
81
112
|
```ruby
|
82
113
|
logging do |l|
|
83
114
|
l.base_filename = 'superlog'
|
@@ -92,6 +123,100 @@ You can access these variables thus:
|
|
92
123
|
MyConfig[:logging][:max_log_files]
|
93
124
|
```
|
94
125
|
|
126
|
+
### Lists of Contexts
|
127
|
+
For use cases where you need to be able to specify a list of things with identical configuration
|
128
|
+
you can define a `context_config_list` like so:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
require 'mixlib/config'
|
132
|
+
|
133
|
+
module MyConfig
|
134
|
+
extend Mixlib::Config
|
135
|
+
|
136
|
+
# The first argument is the plural word for your item, the second is the singular
|
137
|
+
config_context_list :apples, :apple do
|
138
|
+
default :species
|
139
|
+
default :color, 'red'
|
140
|
+
default :crispness, 10
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
With this definition everytime the `apple` is called within the config file it
|
146
|
+
will create a new item that can be configured with a block like so:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
apple do
|
150
|
+
species 'Royal Gala'
|
151
|
+
end
|
152
|
+
apple do
|
153
|
+
species 'Granny Smith'
|
154
|
+
color 'green'
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
You can then iterate over the defined values in code:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
MyConfig.apples.each do |apple|
|
162
|
+
puts "#{apple.species} are #{apple.color}"
|
163
|
+
end
|
164
|
+
|
165
|
+
# => Royal Gala are red
|
166
|
+
# => Granny Smith are green
|
167
|
+
```
|
168
|
+
|
169
|
+
_**Note**: When using the config context lists they must use the [block style](#block-style) or [block with argument style](#block-with-argument-style)_
|
170
|
+
|
171
|
+
### Hashes of Contexts
|
172
|
+
For use cases where you need to be able to specify a list of things with identical configuration
|
173
|
+
that are keyed to a specific value, you can define a `context_config_hash` like so:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
require 'mixlib/config'
|
177
|
+
|
178
|
+
module MyConfig
|
179
|
+
extend Mixlib::Config
|
180
|
+
|
181
|
+
# The first argument is the plural word for your item, the second is the singular
|
182
|
+
config_context_hash :apples, :apple do
|
183
|
+
default :species
|
184
|
+
default :color, 'red'
|
185
|
+
default :crispness, 10
|
186
|
+
end
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
This can then be used in the config file like so:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
apple 'Royal Gala' do
|
194
|
+
species 'Royal Gala'
|
195
|
+
end
|
196
|
+
apple 'Granny Smith' do
|
197
|
+
species 'Granny Smith'
|
198
|
+
color 'green'
|
199
|
+
end
|
200
|
+
|
201
|
+
# You can also reopen a context to edit a value
|
202
|
+
apple 'Royal Gala' do
|
203
|
+
crispness 3
|
204
|
+
end
|
205
|
+
```
|
206
|
+
|
207
|
+
You can then iterate over the defined values in code:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
MyConfig.apples.each do |key, apple|
|
211
|
+
puts "#{key} => #{apple.species} are #{apple.color}"
|
212
|
+
end
|
213
|
+
|
214
|
+
# => Royal Gala => Royal Gala are red
|
215
|
+
# => Granny Smith => Granny Smith are green
|
216
|
+
```
|
217
|
+
|
218
|
+
_**Note**: When using the config context hashes they must use the [block style](#block-style) or [block with argument style](#block-with-argument-style)_
|
219
|
+
|
95
220
|
## Default Values
|
96
221
|
|
97
222
|
Mixlib::Config has a powerful default value facility. In addition to being able to specify explicit default values, you can even specify Ruby code blocks that will run if the config value is not set. This can allow you to build options whose values are based on other options.
|
@@ -147,3 +272,26 @@ Testing your application with different sets of arguments can by simplified with
|
|
147
272
|
NOTE: if you have arrays of arrays, or other deep nesting, we suggest you use code blocks to set up your default values (`default(:option) { [ [ 1, 2 ], [ 3, 4 ] ] }`). Deep children will not always be reset to their default values.
|
148
273
|
|
149
274
|
Enjoy!
|
275
|
+
|
276
|
+
## Contributing
|
277
|
+
|
278
|
+
For information on contributing to this project see <https://github.com/chef/chef/blob/master/CONTRIBUTING.md>
|
279
|
+
|
280
|
+
## License
|
281
|
+
|
282
|
+
- Copyright:: Copyright (c) 2009-2016 Chef Software, Inc.
|
283
|
+
- License:: Apache License, Version 2.0
|
284
|
+
|
285
|
+
```text
|
286
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
287
|
+
you may not use this file except in compliance with the License.
|
288
|
+
You may obtain a copy of the License at
|
289
|
+
|
290
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
291
|
+
|
292
|
+
Unless required by applicable law or agreed to in writing, software
|
293
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
294
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
295
|
+
See the License for the specific language governing permissions and
|
296
|
+
limitations under the License.
|
297
|
+
|
data/Rakefile
CHANGED
@@ -7,7 +7,7 @@ require "mixlib/config/version"
|
|
7
7
|
|
8
8
|
Bundler::GemHelper.install_tasks
|
9
9
|
|
10
|
-
task :
|
10
|
+
task default: [:style, :spec]
|
11
11
|
|
12
12
|
desc "Run specs"
|
13
13
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
@@ -16,23 +16,19 @@ end
|
|
16
16
|
|
17
17
|
gem_spec = eval(File.read("mixlib-config.gemspec"))
|
18
18
|
|
19
|
+
begin
|
20
|
+
require "chefstyle"
|
21
|
+
require "rubocop/rake_task"
|
22
|
+
RuboCop::RakeTask.new(:style) do |task|
|
23
|
+
task.options += ["--display-cop-names", "--no-color"]
|
24
|
+
end
|
25
|
+
rescue LoadError
|
26
|
+
puts "chefstyle/rubocop is not available. gem install chefstyle to do style checking."
|
27
|
+
end
|
28
|
+
|
19
29
|
RDoc::Task.new do |rdoc|
|
20
30
|
rdoc.rdoc_dir = "rdoc"
|
21
31
|
rdoc.title = "mixlib-config #{gem_spec.version}"
|
22
32
|
rdoc.rdoc_files.include("README*")
|
23
33
|
rdoc.rdoc_files.include("lib/**/*.rb")
|
24
34
|
end
|
25
|
-
|
26
|
-
begin
|
27
|
-
require "github_changelog_generator/task"
|
28
|
-
|
29
|
-
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
30
|
-
config.issues = false
|
31
|
-
config.future_release = Mixlib::Config::VERSION
|
32
|
-
config.enhancement_labels = "enhancement,Enhancement,New Feature,Feature".split(",")
|
33
|
-
config.bug_labels = "bug,Bug,Improvement,Upstream Bug".split(",")
|
34
|
-
config.exclude_labels = "duplicate,question,invalid,wontfix,no_changelog,Exclude From Changelog,Question,Discussion".split(",")
|
35
|
-
end
|
36
|
-
rescue LoadError
|
37
|
-
puts "github_changelog_generator is not available. gem install github_changelog_generator to generate changelogs"
|
38
|
-
end
|
data/lib/mixlib/config.rb
CHANGED
@@ -30,10 +30,14 @@ module Mixlib
|
|
30
30
|
class << base; attr_accessor :configuration; end
|
31
31
|
class << base; attr_accessor :configurables; end
|
32
32
|
class << base; attr_accessor :config_contexts; end
|
33
|
+
class << base; attr_accessor :config_context_lists; end
|
34
|
+
class << base; attr_accessor :config_context_hashes; end
|
33
35
|
class << base; attr_accessor :config_parent; end
|
34
36
|
base.configuration = Hash.new
|
35
37
|
base.configurables = Hash.new
|
36
38
|
base.config_contexts = Hash.new
|
39
|
+
base.config_context_lists = Hash.new
|
40
|
+
base.config_context_hashes = Hash.new
|
37
41
|
base.initialize_mixlib_config
|
38
42
|
end
|
39
43
|
|
@@ -49,7 +53,51 @@ module Mixlib
|
|
49
53
|
# === Parameters
|
50
54
|
# filename<String>:: A filename to read from
|
51
55
|
def from_file(filename)
|
52
|
-
|
56
|
+
if %w{ .yml .yaml }.include?(File.extname(filename))
|
57
|
+
from_yaml(filename)
|
58
|
+
elsif File.extname(filename) == ".json"
|
59
|
+
from_json(filename)
|
60
|
+
else
|
61
|
+
instance_eval(IO.read(filename), filename, 1)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Parses valid YAML structure into Ruby so it can be ingested into the Class
|
66
|
+
#
|
67
|
+
# === Parameters
|
68
|
+
# filename<String>:: A filename to read from
|
69
|
+
def from_yaml(filename)
|
70
|
+
require "yaml"
|
71
|
+
from_hash(YAML.load(IO.read(filename)), filename)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Parses valid JSON structure into Ruby
|
75
|
+
#
|
76
|
+
# === Parameters
|
77
|
+
# filename<String>:: A filename to read from
|
78
|
+
def from_json(filename)
|
79
|
+
require "json"
|
80
|
+
from_hash(JSON.parse(IO.read(filename)), filename)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Transforms a Hash into method-style configuration syntax to be processed
|
84
|
+
#
|
85
|
+
# === Parameters
|
86
|
+
# hash<Hash>:: A Hash containing configuration
|
87
|
+
def from_hash(hash, filename = "in_memory")
|
88
|
+
ruby_translation = []
|
89
|
+
|
90
|
+
to_dotted_hash(hash).each do |k, v|
|
91
|
+
if v.is_a? Array
|
92
|
+
ruby_translation << "#{k} #{v}"
|
93
|
+
elsif v.is_a? String
|
94
|
+
ruby_translation << "#{k} \"#{v}\""
|
95
|
+
else
|
96
|
+
ruby_translation << "#{k} #{v}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
instance_eval(ruby_translation.join("\n"), filename, 1)
|
53
101
|
end
|
54
102
|
|
55
103
|
# Pass Mixlib::Config.configure() a block, and it will yield itself
|
@@ -57,7 +105,7 @@ module Mixlib
|
|
57
105
|
# === Parameters
|
58
106
|
# block<Block>:: A block that is called with self.configuration as the argument.
|
59
107
|
def configure(&block)
|
60
|
-
yield(
|
108
|
+
yield(configuration)
|
61
109
|
end
|
62
110
|
|
63
111
|
# Get the value of a config option
|
@@ -98,7 +146,7 @@ module Mixlib
|
|
98
146
|
# <True>:: If the config option exists
|
99
147
|
# <False>:: If the config option does not exist
|
100
148
|
def has_key?(key)
|
101
|
-
|
149
|
+
configuration.has_key?(key.to_sym)
|
102
150
|
end
|
103
151
|
|
104
152
|
# Resets a config option to its default.
|
@@ -106,13 +154,13 @@ module Mixlib
|
|
106
154
|
# === Parameters
|
107
155
|
# symbol<Symbol>:: Name of the config option
|
108
156
|
def delete(symbol)
|
109
|
-
|
157
|
+
configuration.delete(symbol)
|
110
158
|
end
|
111
159
|
|
112
160
|
# Resets all config options to their defaults.
|
113
161
|
def reset
|
114
162
|
self.configuration = Hash.new
|
115
|
-
|
163
|
+
config_contexts.values.each { |config_context| config_context.reset }
|
116
164
|
end
|
117
165
|
|
118
166
|
# Makes a copy of any non-default values.
|
@@ -159,19 +207,31 @@ module Mixlib
|
|
159
207
|
# }
|
160
208
|
#
|
161
209
|
def save(include_defaults = false)
|
162
|
-
result =
|
210
|
+
result = configuration.dup
|
163
211
|
if include_defaults
|
164
|
-
(
|
212
|
+
(configurables.keys - result.keys).each do |missing_default|
|
165
213
|
# Ask any configurables to save themselves into the result array
|
166
|
-
if
|
167
|
-
result[missing_default] =
|
214
|
+
if configurables[missing_default].has_default
|
215
|
+
result[missing_default] = configurables[missing_default].default
|
168
216
|
end
|
169
217
|
end
|
170
218
|
end
|
171
|
-
|
219
|
+
config_contexts.each_pair do |key, context|
|
172
220
|
context_result = context.save(include_defaults)
|
173
221
|
result[key] = context_result if context_result.size != 0 || include_defaults
|
174
222
|
end
|
223
|
+
config_context_lists.each_pair do |key, meta|
|
224
|
+
meta[:values].each do |context|
|
225
|
+
context_result = context.save(include_defaults)
|
226
|
+
result[key] = (result[key] || []) << context_result if context_result.size != 0 || include_defaults
|
227
|
+
end
|
228
|
+
end
|
229
|
+
config_context_hashes.each_pair do |key, meta|
|
230
|
+
meta[:values].each_pair do |context_key, context|
|
231
|
+
context_result = context.save(include_defaults)
|
232
|
+
(result[key] ||= {})[context_key] = context_result if context_result.size != 0 || include_defaults
|
233
|
+
end
|
234
|
+
end
|
175
235
|
result
|
176
236
|
end
|
177
237
|
alias :to_hash :save
|
@@ -180,7 +240,7 @@ module Mixlib
|
|
180
240
|
#
|
181
241
|
# === Parameters
|
182
242
|
# hash<Hash>: a hash in the same format as output by save.
|
183
|
-
#
|
243
|
+
#
|
184
244
|
# === Returns
|
185
245
|
# self
|
186
246
|
def restore(hash)
|
@@ -192,6 +252,26 @@ module Mixlib
|
|
192
252
|
config_context.reset
|
193
253
|
end
|
194
254
|
end
|
255
|
+
config_context_lists.each do |key, meta|
|
256
|
+
meta[:values] = []
|
257
|
+
if hash.has_key?(key)
|
258
|
+
hash[key].each do |val|
|
259
|
+
context = define_context(meta[:definition_blocks])
|
260
|
+
context.restore(val)
|
261
|
+
meta[:values] << context
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
config_context_hashes.each do |key, meta|
|
266
|
+
meta[:values] = {}
|
267
|
+
if hash.has_key?(key)
|
268
|
+
hash[key].each do |vkey, val|
|
269
|
+
context = define_context(meta[:definition_blocks])
|
270
|
+
context.restore(val)
|
271
|
+
meta[:values][vkey] = context
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
195
275
|
end
|
196
276
|
|
197
277
|
# Merge an incoming hash with our config options
|
@@ -203,11 +283,11 @@ module Mixlib
|
|
203
283
|
# self
|
204
284
|
def merge!(hash)
|
205
285
|
hash.each do |key, value|
|
206
|
-
if
|
286
|
+
if config_contexts.has_key?(key)
|
207
287
|
# Grab the config context and let internal_get cache it if so desired
|
208
|
-
|
288
|
+
config_contexts[key].restore(value)
|
209
289
|
else
|
210
|
-
|
290
|
+
configuration[key] = value
|
211
291
|
end
|
212
292
|
end
|
213
293
|
self
|
@@ -221,7 +301,7 @@ module Mixlib
|
|
221
301
|
# === Returns
|
222
302
|
# result of Hash#keys
|
223
303
|
def keys
|
224
|
-
|
304
|
+
configuration.keys
|
225
305
|
end
|
226
306
|
|
227
307
|
# Creates a shallow copy of the internal hash
|
@@ -332,6 +412,70 @@ module Mixlib
|
|
332
412
|
context
|
333
413
|
end
|
334
414
|
|
415
|
+
# Allows you to create a new list of config contexts where you can define new
|
416
|
+
# options with default values.
|
417
|
+
#
|
418
|
+
# This method allows you to open up the configurable more than once.
|
419
|
+
#
|
420
|
+
# For example:
|
421
|
+
#
|
422
|
+
# config_context_list :listeners, :listener do
|
423
|
+
# configurable(:url).defaults_to("http://localhost")
|
424
|
+
# end
|
425
|
+
#
|
426
|
+
# === Parameters
|
427
|
+
# symbol<Symbol>: the plural name for contexts in the list
|
428
|
+
# symbol<Symbol>: the singular name for contexts in the list
|
429
|
+
# block<Block>: a block that will be run in the context of this new config
|
430
|
+
# class.
|
431
|
+
def config_context_list(plural_symbol, singular_symbol, &block)
|
432
|
+
if configurables.has_key?(symbol)
|
433
|
+
raise ReopenedConfigurableWithConfigContextError, "Cannot redefine config value #{plural_symbol} with a config context"
|
434
|
+
end
|
435
|
+
|
436
|
+
unless config_context_lists.has_key?(plural_symbol)
|
437
|
+
config_context_lists[plural_symbol] = {
|
438
|
+
definition_blocks: [],
|
439
|
+
values: [],
|
440
|
+
}
|
441
|
+
define_list_attr_accessor_methods(plural_symbol, singular_symbol)
|
442
|
+
end
|
443
|
+
|
444
|
+
config_context_lists[plural_symbol][:definition_blocks] << block if block_given?
|
445
|
+
end
|
446
|
+
|
447
|
+
# Allows you to create a new hash of config contexts where you can define new
|
448
|
+
# options with default values.
|
449
|
+
#
|
450
|
+
# This method allows you to open up the configurable more than once.
|
451
|
+
#
|
452
|
+
# For example:
|
453
|
+
#
|
454
|
+
# config_context_hash :listeners, :listener do
|
455
|
+
# configurable(:url).defaults_to("http://localhost")
|
456
|
+
# end
|
457
|
+
#
|
458
|
+
# === Parameters
|
459
|
+
# symbol<Symbol>: the plural name for contexts in the list
|
460
|
+
# symbol<Symbol>: the singular name for contexts in the list
|
461
|
+
# block<Block>: a block that will be run in the context of this new config
|
462
|
+
# class.
|
463
|
+
def config_context_hash(plural_symbol, singular_symbol, &block)
|
464
|
+
if configurables.has_key?(symbol)
|
465
|
+
raise ReopenedConfigurableWithConfigContextError, "Cannot redefine config value #{plural_symbol} with a config context"
|
466
|
+
end
|
467
|
+
|
468
|
+
unless config_context_hashes.has_key?(plural_symbol)
|
469
|
+
config_context_hashes[plural_symbol] = {
|
470
|
+
definition_blocks: [],
|
471
|
+
values: {},
|
472
|
+
}
|
473
|
+
define_hash_attr_accessor_methods(plural_symbol, singular_symbol)
|
474
|
+
end
|
475
|
+
|
476
|
+
config_context_hashes[plural_symbol][:definition_blocks] << block if block_given?
|
477
|
+
end
|
478
|
+
|
335
479
|
NOT_PASSED = Object.new
|
336
480
|
|
337
481
|
# Gets or sets strict mode. When strict mode is on, only values which
|
@@ -407,6 +551,27 @@ module Mixlib
|
|
407
551
|
|
408
552
|
private
|
409
553
|
|
554
|
+
# Given a (nested) Hash, turn it into a single top-level hash using dots as
|
555
|
+
# nesting notation. This allows for direction translation into method-style
|
556
|
+
# setting of Config.
|
557
|
+
#
|
558
|
+
# === Parameters
|
559
|
+
# hash<Hash>:: The hash to "de-nestify"
|
560
|
+
# recursive_key<String>:: The existing key to prepend going forward
|
561
|
+
#
|
562
|
+
# === Returns
|
563
|
+
# value:: A single-depth Hash using dot notation to indicate nesting
|
564
|
+
def to_dotted_hash(hash, recursive_key = "")
|
565
|
+
hash.each_with_object({}) do |(k , v), ret|
|
566
|
+
key = recursive_key + k.to_s
|
567
|
+
if v.is_a? Hash
|
568
|
+
ret.merge!(to_dotted_hash(v, key + "."))
|
569
|
+
else
|
570
|
+
ret[key] = v
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
410
575
|
# Internal dispatch setter for config values.
|
411
576
|
#
|
412
577
|
# === Parameters
|
@@ -415,7 +580,7 @@ module Mixlib
|
|
415
580
|
#
|
416
581
|
def internal_set(symbol, value)
|
417
582
|
if configurables.has_key?(symbol)
|
418
|
-
configurables[symbol].set(
|
583
|
+
configurables[symbol].set(configuration, value)
|
419
584
|
elsif config_contexts.has_key?(symbol)
|
420
585
|
config_contexts[symbol].restore(value.to_hash)
|
421
586
|
else
|
@@ -430,9 +595,13 @@ module Mixlib
|
|
430
595
|
|
431
596
|
def internal_get(symbol)
|
432
597
|
if configurables.has_key?(symbol)
|
433
|
-
configurables[symbol].get(
|
598
|
+
configurables[symbol].get(configuration)
|
434
599
|
elsif config_contexts.has_key?(symbol)
|
435
600
|
config_contexts[symbol]
|
601
|
+
elsif config_context_lists.has_key?(symbol)
|
602
|
+
config_context_lists[symbol]
|
603
|
+
elsif config_context_hashes.has_key?(symbol)
|
604
|
+
config_context_hashes[symbol]
|
436
605
|
else
|
437
606
|
if config_strict_mode == :warn
|
438
607
|
Chef::Log.warn("Reading unsupported config value #{symbol}.")
|
@@ -476,5 +645,62 @@ module Mixlib
|
|
476
645
|
end
|
477
646
|
end
|
478
647
|
end
|
648
|
+
|
649
|
+
def define_list_attr_accessor_methods(plural_symbol, singular_symbol)
|
650
|
+
# When Ruby 1.8.7 is no longer supported, this stuff can be done with define_singleton_method!
|
651
|
+
meta = class << self; self; end
|
652
|
+
# Getter for list
|
653
|
+
meta.send :define_method, plural_symbol do
|
654
|
+
internal_get(plural_symbol)[:values]
|
655
|
+
end
|
656
|
+
# Adds a single new context to the list
|
657
|
+
meta.send :define_method, singular_symbol do |&block|
|
658
|
+
context_list_details = internal_get(plural_symbol)
|
659
|
+
new_context = define_context(context_list_details[:definition_blocks])
|
660
|
+
context_list_details[:values] << new_context
|
661
|
+
# If the block expects no arguments, then instance_eval
|
662
|
+
if block.arity == 0
|
663
|
+
new_context.instance_eval(&block)
|
664
|
+
else # yield to the block
|
665
|
+
block.yield(new_context)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
def define_hash_attr_accessor_methods(plural_symbol, singular_symbol)
|
671
|
+
# When Ruby 1.8.7 is no longer supported, this stuff can be done with define_singleton_method!
|
672
|
+
meta = class << self; self; end
|
673
|
+
# Getter for list
|
674
|
+
meta.send :define_method, plural_symbol do
|
675
|
+
internal_get(plural_symbol)[:values]
|
676
|
+
end
|
677
|
+
# Adds a single new context to the list
|
678
|
+
meta.send :define_method, singular_symbol do |key, &block|
|
679
|
+
context_hash_details = internal_get(plural_symbol)
|
680
|
+
context = if context_hash_details[:values].has_key? key
|
681
|
+
context_hash_details[:values][key]
|
682
|
+
else
|
683
|
+
new_context = define_context(context_hash_details[:definition_blocks])
|
684
|
+
context_hash_details[:values][key] = new_context
|
685
|
+
new_context
|
686
|
+
end
|
687
|
+
# If the block expects no arguments, then instance_eval
|
688
|
+
if block.arity == 0
|
689
|
+
context.instance_eval(&block)
|
690
|
+
else # yield to the block
|
691
|
+
block.yield(context)
|
692
|
+
end
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
def define_context(definition_blocks)
|
697
|
+
context = Class.new
|
698
|
+
context.extend(::Mixlib::Config)
|
699
|
+
context.config_parent = self
|
700
|
+
definition_blocks.each do |block|
|
701
|
+
context.instance_eval(&block)
|
702
|
+
end
|
703
|
+
context
|
704
|
+
end
|
479
705
|
end
|
480
706
|
end
|
data/mixlib-config.gemspec
CHANGED
@@ -13,16 +13,18 @@ Gem::Specification.new do |s|
|
|
13
13
|
"LICENSE",
|
14
14
|
"README.md",
|
15
15
|
]
|
16
|
-
s.files = [
|
16
|
+
s.files = ["LICENSE", "NOTICE", "README.md", "Gemfile", "Rakefile"] + Dir.glob("*.gemspec") +
|
17
17
|
Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
|
18
|
-
s.homepage = "
|
18
|
+
s.homepage = "https://www.chef.io"
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
s.rubygems_version = "1.8.23"
|
21
|
+
s.required_ruby_version = ">= 2.2"
|
21
22
|
s.summary = "A class based configuration library"
|
22
23
|
s.description = s.summary
|
23
24
|
s.license = "Apache-2.0"
|
24
25
|
|
25
26
|
s.add_development_dependency "rake"
|
26
27
|
s.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
s.add_development_dependency "chefstyle"
|
27
29
|
s.add_development_dependency "rdoc"
|
28
30
|
end
|
data/spec/mixlib/config_spec.rb
CHANGED
@@ -30,28 +30,28 @@ describe Mixlib::Config do
|
|
30
30
|
allow(File).to receive(:exists?).and_return(true)
|
31
31
|
allow(File).to receive(:readable?).and_return(true)
|
32
32
|
allow(IO).to receive(:read).with("config.rb").and_return("alpha = 'omega'\nfoo = 'bar'")
|
33
|
-
expect(lambda
|
33
|
+
expect(lambda do
|
34
34
|
ConfigIt.from_file("config.rb")
|
35
|
-
|
35
|
+
end).to_not raise_error
|
36
36
|
end
|
37
37
|
|
38
38
|
it "doesn't raise an ArgumentError with an explanation if you try and set a non-existent variable" do
|
39
|
-
expect(lambda
|
39
|
+
expect(lambda do
|
40
40
|
ConfigIt[:foobar] = "blah"
|
41
|
-
|
41
|
+
end).to_not raise_error
|
42
42
|
end
|
43
43
|
|
44
44
|
it "raises an Errno::ENOENT if it can't find the file" do
|
45
|
-
expect(lambda
|
45
|
+
expect(lambda do
|
46
46
|
ConfigIt.from_file("/tmp/timmytimmytimmy")
|
47
|
-
|
47
|
+
end).to raise_error(Errno::ENOENT)
|
48
48
|
end
|
49
49
|
|
50
50
|
it "allows the error to bubble up when it's anything other than IOError" do
|
51
51
|
allow(IO).to receive(:read).with("config.rb").and_return("@#asdf")
|
52
|
-
expect(lambda
|
52
|
+
expect(lambda do
|
53
53
|
ConfigIt.from_file("config.rb")
|
54
|
-
|
54
|
+
end).to raise_error(SyntaxError)
|
55
55
|
end
|
56
56
|
|
57
57
|
it "allows you to reference a value by index" do
|
@@ -796,6 +796,20 @@ describe Mixlib::Config do
|
|
796
796
|
expect(@klass.blah.x).to eql(10)
|
797
797
|
expect(@klass.save).to eql({ :blah => { :x => 10 } })
|
798
798
|
end
|
799
|
+
|
800
|
+
# this tests existing (somewhat bizzare) behavior of mixlib-config where testing to
|
801
|
+
# see if a key exists is equivalent to testing if the key has been set -- we can still
|
802
|
+
# retrieve the default value if it was set. the code in chef/chef which merges
|
803
|
+
# knife config values into cli values will be sensitive to this behavior.
|
804
|
+
it "defaults values do not show up when querying with #has_key?" do
|
805
|
+
expect(@klass.blah.has_key?(:x)).to be false
|
806
|
+
expect(@klass.blah.x).to be 5
|
807
|
+
end
|
808
|
+
|
809
|
+
it "if we assign the values, they show up when querying with #has_key?" do
|
810
|
+
@klass.blah.x = 5
|
811
|
+
expect(@klass.blah.has_key?(:x)).to be true
|
812
|
+
end
|
799
813
|
end
|
800
814
|
|
801
815
|
describe "When a configurable exists with a nested context" do
|
@@ -1003,4 +1017,201 @@ describe Mixlib::Config do
|
|
1003
1017
|
end).to raise_error(Mixlib::Config::ReopenedConfigContextWithConfigurableError)
|
1004
1018
|
end
|
1005
1019
|
|
1020
|
+
describe "config context lists" do
|
1021
|
+
let(:klass) do
|
1022
|
+
klass = Class.new
|
1023
|
+
klass.extend ::Mixlib::Config
|
1024
|
+
klass.instance_eval do
|
1025
|
+
config_context_list(:tests, :test) do
|
1026
|
+
default :y, 20
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
klass
|
1030
|
+
end
|
1031
|
+
it "defines list methods when declaring a config_context_list" do
|
1032
|
+
expect(klass.methods).to include :test
|
1033
|
+
expect(klass.methods).to include :tests
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
it "creates a new item each time the singular list is called" do
|
1037
|
+
klass.test do
|
1038
|
+
y 40
|
1039
|
+
end
|
1040
|
+
klass.test do
|
1041
|
+
y 50
|
1042
|
+
end
|
1043
|
+
expect(klass.tests.length).to be 2
|
1044
|
+
expect(klass.tests.first.y).to be 40
|
1045
|
+
expect(klass.tests.last.y).to be 50
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
it "can save the config list" do
|
1049
|
+
klass.test do
|
1050
|
+
y 40
|
1051
|
+
end
|
1052
|
+
klass.test do
|
1053
|
+
y 50
|
1054
|
+
end
|
1055
|
+
expect(klass.save).to eq({
|
1056
|
+
tests: [
|
1057
|
+
{ y: 40 },
|
1058
|
+
{ y: 50 },
|
1059
|
+
],
|
1060
|
+
})
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
it "can restore the config list from a hash" do
|
1064
|
+
hash = {
|
1065
|
+
tests: [
|
1066
|
+
{ y: 40 },
|
1067
|
+
{ y: 50 },
|
1068
|
+
],
|
1069
|
+
}
|
1070
|
+
klass.restore(hash)
|
1071
|
+
expect(klass.tests.length).to be 2
|
1072
|
+
expect(klass.tests.first.y).to be 40
|
1073
|
+
expect(klass.tests.last.y).to be 50
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
describe "config context hashes" do
|
1078
|
+
let(:klass) do
|
1079
|
+
klass = Class.new
|
1080
|
+
klass.extend ::Mixlib::Config
|
1081
|
+
klass.instance_eval do
|
1082
|
+
config_context_hash(:tests, :test) do
|
1083
|
+
default :y, 20
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
klass
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
it "defines list methods when declaring a config_context_hash" do
|
1090
|
+
expect(klass.methods).to include :test
|
1091
|
+
expect(klass.methods).to include :tests
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
context "when called with a new key each time" do
|
1095
|
+
it "creates a new item each time" do
|
1096
|
+
klass.test :one do
|
1097
|
+
y 40
|
1098
|
+
end
|
1099
|
+
klass.test :two do
|
1100
|
+
y 50
|
1101
|
+
end
|
1102
|
+
expect(klass.tests.length).to be 2
|
1103
|
+
expect(klass.tests[:one].y).to be 40
|
1104
|
+
expect(klass.tests[:two].y).to be 50
|
1105
|
+
end
|
1106
|
+
end
|
1107
|
+
context "when called with the same key" do
|
1108
|
+
it "modifies the existing value" do
|
1109
|
+
klass.test :only do
|
1110
|
+
y 40
|
1111
|
+
end
|
1112
|
+
klass.test :only do
|
1113
|
+
y 50
|
1114
|
+
end
|
1115
|
+
expect(klass.tests.length).to be 1
|
1116
|
+
expect(klass.tests[:only].y).to be 50
|
1117
|
+
end
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
it "can save the config hash" do
|
1121
|
+
klass.test :one do
|
1122
|
+
y 40
|
1123
|
+
end
|
1124
|
+
klass.test :two do
|
1125
|
+
y 50
|
1126
|
+
end
|
1127
|
+
expect(klass.save).to eq({
|
1128
|
+
tests: {
|
1129
|
+
one: { y: 40 },
|
1130
|
+
two: { y: 50 },
|
1131
|
+
},
|
1132
|
+
})
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
it "can restore the config hash from a hash" do
|
1136
|
+
hash = {
|
1137
|
+
tests: {
|
1138
|
+
one: { y: 40 },
|
1139
|
+
two: { y: 50 },
|
1140
|
+
},
|
1141
|
+
}
|
1142
|
+
klass.restore(hash)
|
1143
|
+
expect(klass.tests.length).to be 2
|
1144
|
+
expect(klass.tests[:one].y).to be 40
|
1145
|
+
expect(klass.tests[:two].y).to be 50
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
describe ".from_yaml" do
|
1150
|
+
let(:yaml) do
|
1151
|
+
<<-EOH
|
1152
|
+
---
|
1153
|
+
foo:
|
1154
|
+
- bar
|
1155
|
+
- baz
|
1156
|
+
- matazz
|
1157
|
+
alpha: beta
|
1158
|
+
EOH
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
it "turns YAML into method-style setting" do
|
1162
|
+
allow(File).to receive(:exists?).and_return(true)
|
1163
|
+
allow(File).to receive(:readable?).and_return(true)
|
1164
|
+
allow(IO).to receive(:read).with("config.yml").and_return(yaml)
|
1165
|
+
|
1166
|
+
expect(lambda do
|
1167
|
+
ConfigIt.from_file("config.yml")
|
1168
|
+
end).to_not raise_error
|
1169
|
+
|
1170
|
+
expect(ConfigIt.foo).to eql(%w{ bar baz matazz })
|
1171
|
+
expect(ConfigIt.alpha).to eql("beta")
|
1172
|
+
end
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
describe ".from_json" do
|
1176
|
+
let(:json) do
|
1177
|
+
<<-EOH
|
1178
|
+
{
|
1179
|
+
"foo": [
|
1180
|
+
"bar",
|
1181
|
+
"baz",
|
1182
|
+
"matazz"
|
1183
|
+
],
|
1184
|
+
"alpha": "beta"
|
1185
|
+
}
|
1186
|
+
EOH
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
it "turns YAML into method-style setting" do
|
1190
|
+
allow(File).to receive(:exists?).and_return(true)
|
1191
|
+
allow(File).to receive(:readable?).and_return(true)
|
1192
|
+
allow(IO).to receive(:read).with("config.json").and_return(json)
|
1193
|
+
|
1194
|
+
expect(lambda do
|
1195
|
+
ConfigIt.from_file("config.json")
|
1196
|
+
end).to_not raise_error
|
1197
|
+
|
1198
|
+
expect(ConfigIt.foo).to eql(%w{ bar baz matazz })
|
1199
|
+
expect(ConfigIt.alpha).to eql("beta")
|
1200
|
+
end
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
describe ".from_hash" do
|
1204
|
+
let(:hash) do
|
1205
|
+
{
|
1206
|
+
"alpha" => "beta",
|
1207
|
+
"foo" => %w{ bar baz matazz},
|
1208
|
+
}
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
it "translates the Hash into method-style" do
|
1212
|
+
ConfigIt.from_hash(hash)
|
1213
|
+
expect(ConfigIt.foo).to eql(%w{ bar baz matazz })
|
1214
|
+
expect(ConfigIt.alpha).to eql("beta")
|
1215
|
+
end
|
1216
|
+
end
|
1006
1217
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixlib-config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef Software, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: chefstyle
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rdoc
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,7 +88,7 @@ files:
|
|
74
88
|
- mixlib-config.gemspec
|
75
89
|
- spec/mixlib/config_spec.rb
|
76
90
|
- spec/spec_helper.rb
|
77
|
-
homepage:
|
91
|
+
homepage: https://www.chef.io
|
78
92
|
licenses:
|
79
93
|
- Apache-2.0
|
80
94
|
metadata: {}
|
@@ -86,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
100
|
requirements:
|
87
101
|
- - ">="
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
103
|
+
version: '2.2'
|
90
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
105
|
requirements:
|
92
106
|
- - ">="
|
@@ -94,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
108
|
version: '0'
|
95
109
|
requirements: []
|
96
110
|
rubyforge_project:
|
97
|
-
rubygems_version: 2.
|
111
|
+
rubygems_version: 2.7.4
|
98
112
|
signing_key:
|
99
113
|
specification_version: 4
|
100
114
|
summary: A class based configuration library
|