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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d8af6841c4facccc997e4f034d65114d5cb37493
4
- data.tar.gz: 83cce1c086a8ced0d664a0c69fbda32984abbf49
2
+ SHA256:
3
+ metadata.gz: '05795379d8df703468f3f0dea7e8e28f9229cbca557e1e6ab6b1bf5b36781536'
4
+ data.tar.gz: 951f9d4f144a3d60a46d8c702962c7c9ca08eafbe6a3edd104b32b347691ebb3
5
5
  SHA512:
6
- metadata.gz: ec9e172a49462573a09580ecd568ce2fa8c3827f48d38c93a5288269481d0b023e111a8acadd801f0682f7081ea56a70f9201747d90dff55f09f2867a648256d
7
- data.tar.gz: 46eeefec4cd14ba0283461a319955c444e5ab0b0f4213ed4fe85c309ccf3bc48b0687b0fb2d1b462b4914b2e89b0d3c473194dca3f00b675dd28658671726216
6
+ metadata.gz: eeb6834b5842c7b4ac030f4465a9d54e784cd8925cfc09fb0d307b35241eedd4769abf7eb67ded947093180d53c35491f8b5444e479d75604434aa385df62277
7
+ data.tar.gz: 76e29d1167cfcc8314c1823971c7290fc4da7f403f06a99f42480e9adc1390b70482a192a08a619f11f61568c207a43feec9591e2a97890cf27b645454942505
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
-
5
- gem "github_changelog_generator", group: :changelog
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
- #### Method Style
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
- #### Block Style
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
- #### Block with Argument Style
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 :default => :spec
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
@@ -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
- self.instance_eval(IO.read(filename), filename, 1)
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(self.configuration)
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
- self.configuration.has_key?(key.to_sym)
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
- self.configuration.delete(symbol)
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
- self.config_contexts.values.each { |config_context| config_context.reset }
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 = self.configuration.dup
210
+ result = configuration.dup
163
211
  if include_defaults
164
- (self.configurables.keys - result.keys).each do |missing_default|
212
+ (configurables.keys - result.keys).each do |missing_default|
165
213
  # Ask any configurables to save themselves into the result array
166
- if self.configurables[missing_default].has_default
167
- result[missing_default] = self.configurables[missing_default].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
- self.config_contexts.each_pair do |key, context|
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 self.config_contexts.has_key?(key)
286
+ if config_contexts.has_key?(key)
207
287
  # Grab the config context and let internal_get cache it if so desired
208
- self.config_contexts[key].restore(value)
288
+ config_contexts[key].restore(value)
209
289
  else
210
- self.configuration[key] = value
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
- self.configuration.keys
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(self.configuration, value)
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(self.configuration)
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
@@ -19,7 +19,7 @@
19
19
  module Mixlib
20
20
  module Config
21
21
 
22
- VERSION = "2.2.4"
22
+ VERSION = "2.2.5"
23
23
 
24
24
  end
25
25
  end
@@ -13,16 +13,18 @@ Gem::Specification.new do |s|
13
13
  "LICENSE",
14
14
  "README.md",
15
15
  ]
16
- s.files = [ "LICENSE", "NOTICE", "README.md", "Gemfile", "Rakefile" ] + Dir.glob("*.gemspec") +
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 = "http://www.chef.io"
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
@@ -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
- }).to_not raise_error
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
- }).to_not raise_error
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
- }).to raise_error(Errno::ENOENT)
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
- }).to raise_error(SyntaxError)
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
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: 2016-09-02 00:00:00.000000000 Z
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: http://www.chef.io
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: '0'
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.6.6
111
+ rubygems_version: 2.7.4
98
112
  signing_key:
99
113
  specification_version: 4
100
114
  summary: A class based configuration library