test-kitchen-rsync 3.0.0.pre.1

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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +21 -0
  3. data/LICENSE +15 -0
  4. data/Rakefile +53 -0
  5. data/bin/zl-kitchen +11 -0
  6. data/lib/kitchen/base64_stream.rb +48 -0
  7. data/lib/kitchen/chef_utils_wiring.rb +40 -0
  8. data/lib/kitchen/cli.rb +413 -0
  9. data/lib/kitchen/collection.rb +52 -0
  10. data/lib/kitchen/color.rb +63 -0
  11. data/lib/kitchen/command/action.rb +41 -0
  12. data/lib/kitchen/command/console.rb +54 -0
  13. data/lib/kitchen/command/diagnose.rb +84 -0
  14. data/lib/kitchen/command/doctor.rb +39 -0
  15. data/lib/kitchen/command/exec.rb +37 -0
  16. data/lib/kitchen/command/list.rb +148 -0
  17. data/lib/kitchen/command/login.rb +39 -0
  18. data/lib/kitchen/command/package.rb +32 -0
  19. data/lib/kitchen/command/sink.rb +50 -0
  20. data/lib/kitchen/command/test.rb +47 -0
  21. data/lib/kitchen/command.rb +207 -0
  22. data/lib/kitchen/config.rb +344 -0
  23. data/lib/kitchen/configurable.rb +616 -0
  24. data/lib/kitchen/data_munger.rb +1024 -0
  25. data/lib/kitchen/diagnostic.rb +138 -0
  26. data/lib/kitchen/driver/base.rb +133 -0
  27. data/lib/kitchen/driver/dummy.rb +105 -0
  28. data/lib/kitchen/driver/exec.rb +70 -0
  29. data/lib/kitchen/driver/proxy.rb +70 -0
  30. data/lib/kitchen/driver/ssh_base.rb +351 -0
  31. data/lib/kitchen/driver.rb +40 -0
  32. data/lib/kitchen/errors.rb +243 -0
  33. data/lib/kitchen/generator/init.rb +254 -0
  34. data/lib/kitchen/instance.rb +726 -0
  35. data/lib/kitchen/lazy_hash.rb +148 -0
  36. data/lib/kitchen/lifecycle_hook/base.rb +78 -0
  37. data/lib/kitchen/lifecycle_hook/local.rb +53 -0
  38. data/lib/kitchen/lifecycle_hook/remote.rb +39 -0
  39. data/lib/kitchen/lifecycle_hooks.rb +92 -0
  40. data/lib/kitchen/loader/yaml.rb +377 -0
  41. data/lib/kitchen/logger.rb +422 -0
  42. data/lib/kitchen/logging.rb +52 -0
  43. data/lib/kitchen/login_command.rb +49 -0
  44. data/lib/kitchen/metadata_chopper.rb +49 -0
  45. data/lib/kitchen/platform.rb +64 -0
  46. data/lib/kitchen/plugin.rb +76 -0
  47. data/lib/kitchen/plugin_base.rb +60 -0
  48. data/lib/kitchen/provisioner/base.rb +269 -0
  49. data/lib/kitchen/provisioner/chef/berkshelf.rb +116 -0
  50. data/lib/kitchen/provisioner/chef/common_sandbox.rb +350 -0
  51. data/lib/kitchen/provisioner/chef/policyfile.rb +163 -0
  52. data/lib/kitchen/provisioner/chef_apply.rb +121 -0
  53. data/lib/kitchen/provisioner/chef_base.rb +705 -0
  54. data/lib/kitchen/provisioner/chef_infra.rb +167 -0
  55. data/lib/kitchen/provisioner/chef_solo.rb +82 -0
  56. data/lib/kitchen/provisioner/chef_zero.rb +12 -0
  57. data/lib/kitchen/provisioner/dummy.rb +75 -0
  58. data/lib/kitchen/provisioner/shell.rb +157 -0
  59. data/lib/kitchen/provisioner.rb +42 -0
  60. data/lib/kitchen/rake_tasks.rb +80 -0
  61. data/lib/kitchen/shell_out.rb +90 -0
  62. data/lib/kitchen/ssh.rb +289 -0
  63. data/lib/kitchen/state_file.rb +112 -0
  64. data/lib/kitchen/suite.rb +48 -0
  65. data/lib/kitchen/thor_tasks.rb +63 -0
  66. data/lib/kitchen/transport/base.rb +236 -0
  67. data/lib/kitchen/transport/dummy.rb +78 -0
  68. data/lib/kitchen/transport/exec.rb +145 -0
  69. data/lib/kitchen/transport/ssh.rb +579 -0
  70. data/lib/kitchen/transport/winrm.rb +546 -0
  71. data/lib/kitchen/transport.rb +40 -0
  72. data/lib/kitchen/util.rb +229 -0
  73. data/lib/kitchen/verifier/base.rb +243 -0
  74. data/lib/kitchen/verifier/busser.rb +275 -0
  75. data/lib/kitchen/verifier/dummy.rb +75 -0
  76. data/lib/kitchen/verifier/shell.rb +99 -0
  77. data/lib/kitchen/verifier.rb +39 -0
  78. data/lib/kitchen/version.rb +20 -0
  79. data/lib/kitchen/which.rb +26 -0
  80. data/lib/kitchen.rb +152 -0
  81. data/lib/vendor/hash_recursive_merge.rb +79 -0
  82. data/support/busser_install_command.ps1 +14 -0
  83. data/support/busser_install_command.sh +21 -0
  84. data/support/chef-client-fail-if-update-handler.rb +15 -0
  85. data/support/chef_base_init_command.ps1 +18 -0
  86. data/support/chef_base_init_command.sh +1 -0
  87. data/support/chef_base_install_command.ps1 +85 -0
  88. data/support/chef_base_install_command.sh +229 -0
  89. data/support/download_helpers.sh +109 -0
  90. data/support/dummy-validation.pem +27 -0
  91. data/templates/driver/CHANGELOG.md.erb +3 -0
  92. data/templates/driver/Gemfile.erb +3 -0
  93. data/templates/driver/README.md.erb +64 -0
  94. data/templates/driver/Rakefile.erb +21 -0
  95. data/templates/driver/driver.rb.erb +23 -0
  96. data/templates/driver/gemspec.erb +29 -0
  97. data/templates/driver/gitignore.erb +17 -0
  98. data/templates/driver/license_apachev2.erb +15 -0
  99. data/templates/driver/license_lgplv3.erb +16 -0
  100. data/templates/driver/license_mit.erb +22 -0
  101. data/templates/driver/license_reserved.erb +5 -0
  102. data/templates/driver/tailor.erb +4 -0
  103. data/templates/driver/travis.yml.erb +11 -0
  104. data/templates/driver/version.rb.erb +12 -0
  105. data/templates/init/chefignore.erb +2 -0
  106. data/templates/init/kitchen.yml.erb +18 -0
  107. data/test-kitchen.gemspec +52 -0
  108. metadata +528 -0
@@ -0,0 +1,616 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2014, Fletcher Nichol
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require "thor/util"
19
+
20
+ require_relative "lazy_hash"
21
+
22
+ module Kitchen
23
+ # A mixin for providing configuration-related behavior such as default
24
+ # config (static, computed, inherited), required config, local path
25
+ # expansion, etc.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ module Configurable
29
+ def self.included(base)
30
+ base.extend(ClassMethods)
31
+ end
32
+
33
+ # @return [Kitchen::Instance] the associated instance
34
+ attr_reader :instance
35
+
36
+ # A lifecycle method that should be invoked when the object is about ready
37
+ # to be used. A reference to an Instance is required as configuration
38
+ # dependant data may be access through an Instance. This also acts as a
39
+ # hook point where the object may wish to perform other last minute
40
+ # checks, validations, or configuration expansions.
41
+ #
42
+ # @param instance [Instance] an associated instance
43
+ # @return [self] itself, for use in chaining
44
+ # @raise [ClientError] if instance parameter is nil
45
+ def finalize_config!(instance)
46
+ raise ClientError, "Instance must be provided to #{self}" if instance.nil?
47
+
48
+ @instance = instance
49
+ expand_paths!
50
+ deprecate_config!
51
+ validate_config!
52
+ load_needed_dependencies!
53
+
54
+ self
55
+ end
56
+
57
+ # Provides hash-like access to configuration keys.
58
+ #
59
+ # @param attr [Object] configuration key
60
+ # @return [Object] value at configuration key
61
+ def [](attr)
62
+ config[attr]
63
+ end
64
+
65
+ # @return [TrueClass,FalseClass] true if `:shell_type` is `"bourne"` (or
66
+ # unset, for backwards compatability)
67
+ def bourne_shell?
68
+ ["bourne", nil].include?(instance.platform.shell_type)
69
+ end
70
+
71
+ # Find an appropriate path to a file or directory, based on graceful
72
+ # fallback rules or returns nil if path cannot be determined.
73
+ #
74
+ # Given an instance with suite named `"server"`, a `test_base_path` of
75
+ # `"/a/b"`, and a path segement of `"roles"` then following will be tried
76
+ # in order (first match that exists wins):
77
+ #
78
+ # 1. /a/b/server/roles
79
+ # 2. /a/b/roles
80
+ # 3. $PWD/roles
81
+ #
82
+ # @param path [String] the base path segment to search for
83
+ # @param opts [Hash] options
84
+ # @option opts [Symbol] :type either `:file` or `:directory` (default)
85
+ # @option opts [Symbol] :base_path a custom base path to search under,
86
+ # default uses value from `config[:test_base_path]`
87
+ # @return [String] path to the existing file or directory, or nil if file
88
+ # or directory was not found
89
+ # @raise [UserError] if `config[:test_base_path]` is used and is not set
90
+ def calculate_path(path, opts = {})
91
+ type = opts.fetch(:type, :directory)
92
+ base = opts.fetch(:base_path) do
93
+ config.fetch(:test_base_path) do |key|
94
+ raise UserError, "#{key} is not found in #{self}"
95
+ end
96
+ end
97
+
98
+ [
99
+ File.join(base, instance.suite.name, path),
100
+ File.join(base, path),
101
+ File.join(Dir.pwd, path),
102
+ ].find do |candidate|
103
+ type == :directory ? File.directory?(candidate) : File.file?(candidate)
104
+ end
105
+ end
106
+
107
+ # Returns an array of configuration keys.
108
+ #
109
+ # @return [Array] array of configuration keys
110
+ def config_keys
111
+ config.keys
112
+ end
113
+
114
+ # Returns a Hash of configuration and other useful diagnostic information.
115
+ #
116
+ # @return [Hash] a diagnostic hash
117
+ def diagnose
118
+ result = {}
119
+ config_keys.sort.each { |k| result[k] = config[k] }
120
+ result
121
+ end
122
+
123
+ # Returns a Hash of configuration and other useful diagnostic information
124
+ # associated with the plugin itself (such as loaded version, class name,
125
+ # etc.).
126
+ #
127
+ # @return [Hash] a diagnostic hash
128
+ def diagnose_plugin
129
+ result = {}
130
+ result[:name] = name
131
+ result.merge!(self.class.diagnose)
132
+ result
133
+ end
134
+
135
+ # Returns the name of this plugin, suitable for display in a CLI.
136
+ #
137
+ # @return [String] name of this plugin
138
+ def name
139
+ self.class.name.split("::").last
140
+ end
141
+
142
+ # @return [TrueClass,FalseClass] true if `:shell_type` is `"powershell"`
143
+ def powershell_shell?
144
+ ["powershell"].include?(instance.platform.shell_type)
145
+ end
146
+
147
+ # Builds a file path based on the `:os_type` (`"windows"` or `"unix"`).
148
+ #
149
+ # @return [String] joined path for instance's os_type
150
+ def remote_path_join(*parts)
151
+ path = File.join(*parts)
152
+ windows_os? ? path.tr("/", "\\") : path.tr("\\", "/")
153
+ end
154
+
155
+ # @return [TrueClass,FalseClass] true if `:os_type` is `"unix"` (or
156
+ # unset, for backwards compatibility)
157
+ def unix_os?
158
+ ["unix", nil].include?(instance.platform.os_type)
159
+ end
160
+
161
+ # Performs whatever tests that may be required to ensure that this plugin
162
+ # will be able to function in the current environment. This may involve
163
+ # checking for the presence of certain directories, software installed,
164
+ # etc.
165
+ #
166
+ # @raise [UserError] if the plugin will not be able to perform or if a
167
+ # documented dependency is missing from the system
168
+ def verify_dependencies
169
+ # this method may be left unimplemented if that is applicable
170
+ end
171
+
172
+ # @return [TrueClass,FalseClass] true if `:os_type` is `"windows"`
173
+ def windows_os?
174
+ ["windows"].include?(instance.platform.os_type)
175
+ end
176
+
177
+ private
178
+
179
+ # @return [LzayHash] a configuration hash
180
+ # @api private
181
+ attr_reader :config
182
+
183
+ # @return [Hash] a hash of the detected deprecated config attributes
184
+ # @api private
185
+ attr_reader :deprecated_config
186
+
187
+ # @return [Hash] user provided configuration hash
188
+ # @api private
189
+ attr_reader :provided_config
190
+
191
+ # Initializes an internal configuration hash. The hash may contain
192
+ # callable blocks as values that are meant to be called lazily. This
193
+ # method is intended to be included in an object's .initialize method.
194
+ #
195
+ # @param config [Hash] initial provided configuration
196
+ # @api private
197
+ def init_config(config)
198
+ @config = LazyHash.new(config, self)
199
+ @provided_config = config.dup
200
+ self.class.defaults.each do |attr, value|
201
+ @config[attr] = value unless @config.key?(attr)
202
+ end
203
+ end
204
+
205
+ # Expands file paths for certain configuration values. A configuration
206
+ # value is marked for file expansion with a expand_path_for declaration
207
+ # in the included class.
208
+ #
209
+ # @api private
210
+ def expand_paths!
211
+ root_path = config[:kitchen_root] || Dir.pwd
212
+ expanded_paths = LazyHash.new(self.class.expanded_paths, self).to_hash
213
+
214
+ expanded_paths.each do |key, should_expand|
215
+ next if !should_expand || config[key].nil? || config[key] == false
216
+
217
+ config[key] = if config[key].is_a?(Array)
218
+ config[key].map { |path| File.expand_path(path, root_path) }
219
+ else
220
+ File.expand_path(config[key], root_path)
221
+ end
222
+ end
223
+ end
224
+
225
+ # Initialize detected deprecated configuration hash.
226
+ # Display warning if deprecations have been detected.
227
+ #
228
+ # @api private
229
+ def deprecate_config!
230
+ # We only want to display the deprecation list of config values once per execution on the default config.
231
+ # This prevents the output of deprecations from being printed for each permutation of test suites.
232
+ return if defined? @@has_been_warned_of_deprecations
233
+
234
+ deprecated_attributes = LazyHash.new(self.class.deprecated_attributes, self)
235
+ # Remove items from hash when not provided in the loaded config or when the rendered message is nil
236
+ @deprecated_config = deprecated_attributes.delete_if { |attr, obj| !provided_config.key?(attr) || obj.nil? }
237
+
238
+ unless deprecated_config.empty?
239
+ warning = Util.outdent!(<<-MSG)
240
+ Deprecated configuration detected:
241
+ #{deprecated_config.keys.join("\n")}
242
+ Run 'kitchen doctor' for details.
243
+ MSG
244
+ warn(warning)
245
+
246
+ # Set global var that the deprecation message has been printed
247
+ @@has_been_warned_of_deprecations = true
248
+ end
249
+ end
250
+
251
+ # Loads any required third party Ruby libraries or runs any shell out
252
+ # commands to prepare the plugin. This method will be called in the
253
+ # context of the main thread of execution and so does not necessarily
254
+ # have to be thread safe.
255
+ #
256
+ # **Note:** any subclasses overriding this method would be well advised
257
+ # to call super when overriding this method, for example:
258
+ #
259
+ # @example overriding `#load_needed_dependencies!`
260
+ #
261
+ # class MyProvisioner < Kitchen::Provisioner::Base
262
+ # def load_needed_dependencies!
263
+ # super
264
+ # # any further work
265
+ # end
266
+ # end
267
+ #
268
+ # @raise [ClientError] if any library loading fails or any of the
269
+ # dependency requirements cannot be satisfied
270
+ # @api private
271
+ def load_needed_dependencies!
272
+ # this method may be left unimplemented if that is applicable
273
+ end
274
+
275
+ # @return [Logger] the instance's logger or Test Kitchen's common logger
276
+ # otherwise
277
+ # @api private
278
+ def logger
279
+ instance ? instance.logger : Kitchen.logger
280
+ end
281
+
282
+ # @return [String] a powershell command to reload the `PATH` environment
283
+ # variable, only to be used to support old Omnibus Chef packages that
284
+ # require `PATH` to find the `ruby.exe` binary
285
+ # @api private
286
+ def reload_ps1_path
287
+ [
288
+ "$env:PATH = try {",
289
+ "[System.Environment]::GetEnvironmentVariable('PATH','Machine')",
290
+ "} catch { $env:PATH }\n\n",
291
+ ].join("\n")
292
+ end
293
+
294
+ # Builds a shell environment variable assignment string for the
295
+ # required shell type.
296
+ #
297
+ # @param name [String] variable name
298
+ # @param value [String] variable value
299
+ # @return [String] shell variable assignment
300
+ # @api private
301
+ def shell_env_var(name, value)
302
+ if powershell_shell?
303
+ shell_var("env:#{name}", value)
304
+ else
305
+ "#{shell_var(name, value)}; export #{name}"
306
+ end
307
+ end
308
+
309
+ # Builds a shell variable assignment string for the required shell type.
310
+ #
311
+ # @param name [String] variable name
312
+ # @param value [String] variable value
313
+ # @return [String] shell variable assignment
314
+ # @api private
315
+ def shell_var(name, value)
316
+ if powershell_shell?
317
+ %{$#{name} = "#{value}"}
318
+ else
319
+ %{#{name}="#{value}"}
320
+ end
321
+ end
322
+
323
+ # Runs all validations set up for the included class. Each validation is
324
+ # for a specific configuration attribute and has an associated callable
325
+ # block. Each validation block is called with the attribute, its value,
326
+ # and the included object for context.
327
+ #
328
+ # @api private
329
+ def validate_config!
330
+ self.class.validations.each do |attr, block|
331
+ block.call(attr, config[attr], self)
332
+ end
333
+ end
334
+
335
+ # Wraps a body of shell code with common context appropriate for the type
336
+ # of shell.
337
+ #
338
+ # @param code [String] the shell code to be wrapped
339
+ # @return [String] wrapped shell code
340
+ # @api private
341
+ def wrap_shell_code(code)
342
+ return env_wrapped(code) if powershell_shell?
343
+
344
+ Util.wrap_command((env_wrapped code))
345
+ end
346
+
347
+ def env_wrapped(code)
348
+ code_parts = resolve_proxy_settings_from_config
349
+ code_parts << shell_env_var("TEST_KITCHEN", 1)
350
+ code_parts << shell_env_var("CI", ENV["CI"]) if ENV["CI"]
351
+ code_parts << shell_env_var("CHEF_LICENSE", ENV["CHEF_LICENSE"]) if ENV["CHEF_LICENSE"]
352
+ ENV.select { |key, value| key.start_with?("TKENV_") }.each do |key, value|
353
+ env_var_name = "#{key}".sub!("TKENV_", "")
354
+ code_parts << shell_env_var(env_var_name, value)
355
+ end
356
+
357
+ code_parts << code
358
+ code_parts.join("\n")
359
+ end
360
+
361
+ def proxy_setting_keys
362
+ %i{http_proxy https_proxy ftp_proxy no_proxy}
363
+ end
364
+
365
+ def resolve_proxy_settings_from_config
366
+ proxy_setting_keys.each_with_object([]) do |protocol, set_env|
367
+ if !config.key?(protocol) || config[protocol].nil?
368
+ export_proxy(set_env, protocol)
369
+ elsif proxy_config_setting_present?(protocol)
370
+ set_env << shell_env_var(protocol.downcase.to_s, config[protocol])
371
+ set_env << shell_env_var(protocol.upcase.to_s, config[protocol])
372
+ end
373
+ end
374
+ end
375
+
376
+ def proxy_from_config?
377
+ proxy_setting_keys.any? do |protocol|
378
+ !config[protocol].nil?
379
+ end
380
+ end
381
+
382
+ def proxy_from_environment?
383
+ proxy_setting_keys.any? do |protocol|
384
+ !ENV[protocol.downcase.to_s].nil? || !ENV[protocol.upcase.to_s].nil?
385
+ end
386
+ end
387
+
388
+ def proxy_config_setting_present?(protocol)
389
+ config.key?(protocol) && !config[protocol].nil? && !config[protocol].empty?
390
+ end
391
+
392
+ # Helper method to export
393
+ #
394
+ # @param env [Array] the environment to modify
395
+ # @param code [String] the type of proxy to export, one of 'http', 'https' or 'ftp'
396
+ # @api private
397
+ def export_proxy(env, type)
398
+ env << shell_env_var(type.to_s, ENV[type.to_s]) if ENV[type.to_s]
399
+ env << shell_env_var(type.upcase.to_s, ENV[type.upcase.to_s]) if
400
+ ENV[type.upcase.to_s]
401
+ end
402
+
403
+ # Class methods which will be mixed in on inclusion of Configurable module.
404
+ module ClassMethods
405
+ # Sets the loaded version of this plugin, usually corresponding to the
406
+ # RubyGems version of the plugin's library. If the plugin does not set
407
+ # this value, then `nil` will be used and reported.
408
+ #
409
+ # @example setting a version used by RubyGems
410
+ #
411
+ # require "kitchen/driver/vagrant_version"
412
+ #
413
+ # module Kitchen
414
+ # module Driver
415
+ # class Vagrant < Kitchen::Driver::Base
416
+ #
417
+ # plugin_version Kitchen::Driver::VAGRANT_VERSION
418
+ #
419
+ # end
420
+ # end
421
+ # end
422
+ #
423
+ # @param version [String] a version string
424
+ def plugin_version(version) # rubocop:disable Style/TrivialAccessors
425
+ @plugin_version = version
426
+ end
427
+
428
+ # Returns a Hash of configuration and other useful diagnostic
429
+ # information.
430
+ #
431
+ # @return [Hash] a diagnostic hash
432
+ def diagnose
433
+ {
434
+ class: name,
435
+ version: @plugin_version ||= nil,
436
+ api_version: @api_version ||= nil,
437
+ }
438
+ end
439
+
440
+ # Sets a sane default value for a configuration attribute. These values
441
+ # can be overridden by provided configuration or in a subclass with
442
+ # another default_config declaration.
443
+ #
444
+ # @example a nil default value
445
+ #
446
+ # default_config :i_am_nil
447
+ #
448
+ # @example a primitive default value
449
+ #
450
+ # default_config :use_sudo, true
451
+ #
452
+ # @example a block to compute a default value
453
+ #
454
+ # default_config :box_name do |subject|
455
+ # subject.instance.platform.name
456
+ # end
457
+ #
458
+ # @param attr [String] configuration attribute name
459
+ # @param value [Object, nil] static default value for attribute
460
+ # @yieldparam object [Object] a reference to the instantiated object
461
+ # @yieldreturn [Object, nil] dynamically computed value for the attribute
462
+ def default_config(attr, value = nil, &block)
463
+ defaults[attr] = block_given? ? block : value
464
+ end
465
+
466
+ # Ensures that an attribute which is a path will be fully expanded at
467
+ # the right time. This helps make the configuration unambiguous and much
468
+ # easier to debug and diagnose.
469
+ #
470
+ # Note that the file path expansion is only intended for paths on the
471
+ # local workstation invking the Test Kitchen code.
472
+ #
473
+ # @example the default usage
474
+ #
475
+ # expand_path_for :data_path
476
+ #
477
+ # @example disabling path expansion with a falsey value
478
+ #
479
+ # expand_path_for :relative_path, false
480
+ #
481
+ # @example using a block to determine whether or not to expand
482
+ #
483
+ # expand_path_for :relative_or_not_path do |subject|
484
+ # subject.instance.name =~ /default/
485
+ # end
486
+ #
487
+ # @param attr [String] configuration attribute name
488
+ # @param value [Object, nil] whether or not to exand the file path
489
+ # @yieldparam object [Object] a reference to the instantiated object
490
+ # @yieldreturn [Object, nil] dynamically compute whether or not to
491
+ # perform the file expansion
492
+ def expand_path_for(attr, value = true, &block)
493
+ expanded_paths[attr] = block_given? ? block : value
494
+ end
495
+
496
+ # Set the appropriate deprecation message for a given attribute name
497
+ #
498
+ # @example the default usage
499
+ #
500
+ # deprecate_config_for :attribute_name, "Detailed deprecation message."
501
+ #
502
+ # @example using a block
503
+ #
504
+ # deprecate_config_for :attribute_name do |subject|
505
+ # "Detailed deprecation message." if subject == true
506
+ # end
507
+ #
508
+ # @param attr [String] configuration attribute name
509
+ # @param value [Object, nil] static default value for attribute
510
+ # @yieldparam object [Object] a reference to the instantiated object
511
+ # @yieldreturn [Object, nil] dynamically computed value for the attribute
512
+ def deprecate_config_for(attr, value = nil, &block)
513
+ deprecated_attributes[attr] = block_given? ? block : value
514
+ end
515
+
516
+ # Ensures that an attribute must have a non-nil, non-empty String value.
517
+ # The default behavior will be to raise a user error and thereby halting
518
+ # further configuration processing. Good use cases for require_config
519
+ # might be cloud provider credential keys and other similar data.
520
+ #
521
+ # @example a value that must not be nil or an empty String
522
+ #
523
+ # required_config :cloud_api_token
524
+ #
525
+ # @example using a block to use custom validation logic
526
+ #
527
+ # required_config :email do |attr, value, subject|
528
+ # raise UserError, "Must be an email address" unless value =~ /@/
529
+ # end
530
+ #
531
+ # @param attr [String] configuration attribute name
532
+ # @yieldparam attr [Symbol] the attribute name
533
+ # @yieldparam value [Object] the current value of the attribute
534
+ # @yieldparam object [Object] a reference to the instantiated object
535
+ def required_config(attr, &block)
536
+ unless block_given?
537
+ klass = self
538
+ block = lambda do |_, value, thing|
539
+ if value.nil? || value.to_s.empty?
540
+ attribute = "#{klass}#{thing.instance.to_str}#config[:#{attr}]"
541
+ raise UserError, "#{attribute} cannot be blank"
542
+ end
543
+ end
544
+ end
545
+ validations[attr] = block
546
+ end
547
+
548
+ # @return [Hash] a hash of attribute keys and default values which has
549
+ # been merged with any superclass defaults
550
+ # @api private
551
+ def defaults
552
+ @defaults ||= {}.merge(super_defaults)
553
+ end
554
+
555
+ # @return [Hash] a hash of defaults from the included class' superclass
556
+ # if defined in the superclass, or an empty hash otherwise
557
+ # @api private
558
+ def super_defaults
559
+ if superclass.respond_to?(:defaults)
560
+ superclass.defaults
561
+ else
562
+ {}
563
+ end
564
+ end
565
+
566
+ # @return [Hash] a hash of attribute keys and truthy/falsey values to
567
+ # determine if said attribute needs to be fully file path expanded,
568
+ # which has been merged with any superclass expanded paths
569
+ # @api private
570
+ def expanded_paths
571
+ @expanded_paths ||= {}.merge(super_expanded_paths)
572
+ end
573
+
574
+ # @return [Hash] a hash of expanded paths from the included class'
575
+ # superclass if defined in the superclass, or an empty hash otherwise
576
+ # @api private
577
+ def super_expanded_paths
578
+ if superclass.respond_to?(:expanded_paths)
579
+ superclass.expanded_paths
580
+ else
581
+ {}
582
+ end
583
+ end
584
+
585
+ def deprecated_attributes
586
+ @deprecated_attributes ||= {}.merge(super_deprecated_attributes)
587
+ end
588
+
589
+ def super_deprecated_attributes
590
+ if superclass.respond_to?(:deprecated_attributes)
591
+ superclass.deprecated_attributes
592
+ else
593
+ {}
594
+ end
595
+ end
596
+
597
+ # @return [Hash] a hash of attribute keys and valudation callable blocks
598
+ # which has been merged with any superclass valudations
599
+ # @api private
600
+ def validations
601
+ @validations ||= {}.merge(super_validations)
602
+ end
603
+
604
+ # @return [Hash] a hash of validations from the included class'
605
+ # superclass if defined in the superclass, or an empty hash otherwise
606
+ # @api private
607
+ def super_validations
608
+ if superclass.respond_to?(:validations)
609
+ superclass.validations
610
+ else
611
+ {}
612
+ end
613
+ end
614
+ end
615
+ end
616
+ end