rspec-puppet-yaml 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rspec/puppet/yaml"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,326 @@
1
+ module RSpec::Puppet
2
+ # A collection of static methods that simplify creating and building rodjek's
3
+ # RSpec::Puppet::*Matchers. With `MatcherHelpers`, you need call only its
4
+ # `.get_matcher_for` function with arguments appropriate to the matcher you
5
+ # desire. Otherwise, you have to know in advance which matcher you want and,
6
+ # at first, the relationship between method, matcher, and namespace just isn't
7
+ # always obvious.
8
+ #
9
+ # @see https://github.com/rodjek/rspec-puppet
10
+ class MatcherHelpers
11
+ # Attempts to create and return an appropriate RSpec::Puppet::*Matcher for a
12
+ # known matching method and its arguments. If tests are provided and the
13
+ # matcher supports them, the matcher will be built up with the tests.
14
+ #
15
+ # @param method [Variant[String,Symbol]] A recognizable RSpec::Puppet
16
+ # matcher; one of: contain_*, create_*, have_*_count, compile, run, or
17
+ # be_valid_type (where * is a known resource type).
18
+ # @param args [Array[Any]] Arguments to pass to the matcher during
19
+ # construction, if it accepts any.
20
+ # @param tests [Optional[Hash[Variant[String,Symbol],Any]]] Set of unit
21
+ # tests to apply to the matcher, expressed as `:method_call => value(s)`
22
+ # tuples. Use `nil` as the value for method_calls that don't accept
23
+ # arguments.
24
+ # @return [Object] An RSpec::Puppet matcher that knows how to handle
25
+ # `method` and is loaded with `tests`.
26
+ # @raise [NameError] when `method` is unrecognizable.
27
+ def self.get_matcher_for(method, args, tests = {})
28
+ method_str = method.nil? ? 'nil' : method.to_s
29
+ method_sym = method.nil? ? :nil : method.to_sym
30
+
31
+ case method_str
32
+ when /^(contain|create)_.+$/
33
+ matcher = RSpec::Puppet::MatcherHelpers.get_contain_matcher(
34
+ method_sym,
35
+ args,
36
+ tests
37
+ )
38
+ when /^have_.+_count$/
39
+ matcher = RSpec::Puppet::MatcherHelpers.get_count_matcher(
40
+ method_sym,
41
+ args,
42
+ tests
43
+ )
44
+ when 'compile'
45
+ matcher = RSpec::Puppet::MatcherHelpers.get_compile_matcher(
46
+ method_sym,
47
+ args,
48
+ tests
49
+ )
50
+ when 'run'
51
+ matcher = RSpec::Puppet::MatcherHelpers.get_function_matcher(
52
+ method_sym,
53
+ args,
54
+ tests
55
+ )
56
+ when 'be_valid_type'
57
+ matcher = RSpec::Puppet::MatcherHelpers.get_type_matcher(
58
+ method_sym,
59
+ args,
60
+ tests
61
+ )
62
+ else
63
+ raise NameError, "Unknown matcher method: #{method_str}. See http://rspec-puppet.com/matchers/ for valid matchers."
64
+ end
65
+
66
+ matcher
67
+ end
68
+
69
+ # Gets a matcher for :create_* and :contain_* tests.
70
+ #
71
+ # @param method [Symbol] The full :create_* or :contain_* matcher type.
72
+ # @param args [Array[Any]] Must be at least an Array with one element and
73
+ # the first element must be the title of the resource under test. So, for
74
+ # `package { 'my-package': ensure => 'latest', provider => 'apt' }`, this
75
+ # must be set to `[ "my-package" ]`.
76
+ # @param tests [Optional[Hash[Variant[String,Symbol],Any]]] Set of unit
77
+ # tests to apply to the matcher, expressed as `:method_call => value(s)`
78
+ # tuples. Use `nil` as the value for method_calls that don't accept
79
+ # arguments.
80
+ # @raise [ArgumentError] when a resource title is not supplied at args[0].
81
+ #
82
+ # @example package { 'my-package': ensure => 'latest', provider => 'apt' }
83
+ # # Using the all-in-one `with` test
84
+ # matcher = RSpec::Puppet::MatcherHelpers.get_contain_matcher(
85
+ # :contain_package,
86
+ # [ 'my-package' ],
87
+ # { :with => {
88
+ # :ensure => 'latest',
89
+ # :provider => 'apt' }
90
+ # }
91
+ # )
92
+ #
93
+ # # Using individual with_* tests
94
+ # matcher = RSpec::Puppet::MatcherHelpers.get_contain_matcher(
95
+ # :contain_package,
96
+ # [ 'my-package' ],
97
+ # { :with_ensure => 'latest',
98
+ # :with_provider => 'apt'
99
+ # }
100
+ # )
101
+ def self.get_contain_matcher(method, args, tests = {})
102
+ # For this matcher, args[0] must be a String value that is the title for
103
+ # the resource under test.
104
+ if args.nil? || !args
105
+ raise ArgumentError, "Contain/Create matchers require that the first argument be the title of the resource under test."
106
+ elsif !args.kind_of?(Array)
107
+ args = [ args.to_s ]
108
+ end
109
+
110
+ matcher = RSpec::Puppet::ManifestMatchers::CreateGeneric.new(
111
+ method,
112
+ args[0]
113
+ )
114
+
115
+ # Try to be lenient on the Hash requirement for tests
116
+ if tests.kind_of?(Array)
117
+ tests.each { |test| matcher.send(test.to_sym) }
118
+ elsif tests.is_a?(Hash)
119
+ tests.each do |k,v|
120
+ if v.nil? || 'nil' == v.to_s
121
+ matcher.send(k.to_sym)
122
+ else
123
+ matcher.send(k.to_sym, v)
124
+ end
125
+ end
126
+ elsif !tests.nil?
127
+ # Anything left is assumed to be an 'ensure' test
128
+ matcher.send(:with_ensure, tests)
129
+ end
130
+
131
+ matcher
132
+ end
133
+
134
+ # Gets a matcher for have_*_resource_count tests.
135
+ #
136
+ # @param method [Symbol] The full :have_*_resource_count matcher type.
137
+ # @param args [Variant[Integer,Array[Integer]]] May be either an Integer or
138
+ # an Array with exactly one element that is an Integer. This is the number
139
+ # of the expected resource counted.
140
+ # @param tests [Optional[Hash[Variant[String,Symbol],Any]]] *IGNORED* in
141
+ # this version! Should this matcher ever later support additional tests,
142
+ # this will become the set of unit tests to apply to the matcher, expressed
143
+ # as `:method_call => value(s)`` tuples. Use `nil` as the value for
144
+ # method_calls that don't accept arguments.
145
+ #
146
+ # @example 1 package expected (count as an Array element)
147
+ # matcher = RSpec::Puppet::MatcherHelpers.get_count_matcher(
148
+ # :have_package_resource_count,
149
+ # [ 1 ]
150
+ # )
151
+ #
152
+ # @example 12 files expected (count as a bare Integer)
153
+ # matcher = RSpec::Puppet::MatcherHelpers.get_count_matcher(
154
+ # :have_file_resource_count,
155
+ # 12
156
+ # )
157
+ def self.get_count_matcher(method, args, tests = {})
158
+ # This constructor is backward from all the rest and a bit qwirky in that
159
+ # it inexplicably expects method to be an Array and args to be an Integer
160
+ # rather than an Array like all the other matchers. Further, this one
161
+ # expects to receive method twice, as the first and third parameters or
162
+ # part of method as the first and the whole method as the third or nil as
163
+ # the first and method as the third. Quite qwirky, indeed! This helper
164
+ # will steer clear of this qwirkiness by simply passing nil as the first
165
+ # and ensuring that args can be cast as an Integer.
166
+ begin
167
+ if args.kind_of?(Array)
168
+ # Assume the first element of an args array is the intended count
169
+ count = args[0].to_i
170
+ else
171
+ count = args.to_i
172
+ end
173
+ rescue
174
+ raise ArgumentError, "The argument to Count matchers must be a single Integer value."
175
+ end
176
+
177
+ RSpec::Puppet::ManifestMatchers::CountGeneric.new(
178
+ nil,
179
+ count,
180
+ method
181
+ )
182
+ end
183
+
184
+ # Gets a matcher for compile tests.
185
+ #
186
+ # @param method [Symbol] The :compile matcher type.
187
+ # @param args [Optional[Variant[Integer,Array[Integer]]]] **IGNORED** in
188
+ # this version! Should a future version of the compile matcher support
189
+ # constructor arguments, this will become useful.
190
+ # @param tests [Optional[Hash[Variant[String,Symbol],Any]]] Set of unit
191
+ # tests to apply to the matcher, expressed as `:method_call => value(s)`
192
+ # tuples. Use `nil` as the value for method_calls that don't accept
193
+ # arguments.
194
+ #
195
+ # @example The class compiles with all dependencies, spelled out
196
+ # matcher = RSpec::Puppet::MatcherHelpers.get_compile_matcher(
197
+ # :compile,
198
+ # nil,
199
+ # { :with_all_deps => nil }
200
+ # )
201
+ #
202
+ # @example The class compiles with all dependencies, simpler
203
+ # matcher = RSpec::Puppet::MatcherHelpers.get_compile_matcher(
204
+ # :compile,
205
+ # nil,
206
+ # true
207
+ # )
208
+ def self.get_compile_matcher(method, args = [], tests = {})
209
+ matcher = RSpec::Puppet::ManifestMatchers::Compile.new
210
+
211
+ if tests.kind_of?(Array)
212
+ tests.each { |test| matcher.send(test.to_sym) }
213
+ elsif tests.is_a?(Hash)
214
+ tests.each do |k,v|
215
+ if v.nil? || 'nil' == v.to_s
216
+ matcher.send(k.to_sym)
217
+ else
218
+ matcher.send(k.to_sym, v)
219
+ end
220
+ end
221
+ elsif tests.is_a?(String) || tests.is_a?(Symbol)
222
+ matcher.send(tests)
223
+ elsif !tests.nil?
224
+ # Anything left is assumed to be a 'with_all_deps' test...
225
+ if tests
226
+ # ...as long as it is "truthy"
227
+ matcher.send(:with_all_deps)
228
+ end
229
+ end
230
+
231
+ matcher
232
+ end
233
+
234
+ # Gets a matcher for function (:run) tests.
235
+ #
236
+ # @param method [Symbol] The :run matcher type.
237
+ # @param args [Optional[Variant[Integer,Array[Integer]]]] **IGNORED** in
238
+ # this version! Should a future version of the compile matcher support
239
+ # constructor arguments, this will become useful.
240
+ # @param tests [Optional[Variant[Symbol,String,Array[Any],Hash[Any,Any]]]]
241
+ # Set of unit tests to apply to the matcher. Many forms of expressing
242
+ # these tests is supported, though the best fit is Array[Any], which is
243
+ # passed as-is to the function under test as its parameters.
244
+ #
245
+ # @example Test a function that strips known extensions off file-names
246
+ # matcher = RSpec::Puppet::MatcherHelpers.get_function_matcher(
247
+ # :run,
248
+ # nil,
249
+ # [ '/some/arbitrary/path.ext', 'ext' ]
250
+ # )
251
+ def self.get_function_matcher(method, args = [], tests = [])
252
+ matcher = RSpec::Puppet::FunctionMatchers::Run.new
253
+
254
+ if tests.is_a?(Hash)
255
+ tests.each do |k,v|
256
+ if v.nil? || 'nil' == v.to_s
257
+ matcher.send(k.to_sym)
258
+ else
259
+ matcher.send(k.to_sym, v)
260
+ end
261
+ end
262
+ elsif tests.is_a?(String) || tests.is_a?(Symbol)
263
+ matcher.send(tests)
264
+ elsif !tests.nil?
265
+ # Anything left is assumed to be a 'with_params' test, which expects an
266
+ # Array.
267
+ if tests.kind_of?(Array)
268
+ matcher.send(:with_params, tests)
269
+ else
270
+ matcher.send(:with_params, [tests])
271
+ end
272
+ end
273
+
274
+ matcher
275
+ end
276
+
277
+ # Gets a matcher for custom type (:be_valid_type) tests.
278
+ #
279
+ # @param method [Symbol] The :be_valid_type matcher type.
280
+ # @param args [Optional[Variant[Integer,Array[Integer]]]] **IGNORED** in
281
+ # this version! Should a future version of
282
+ # RSpec::Puppet::TypeMatchers::CreateGeneric support constructor arguments,
283
+ # this will become useful.
284
+ # @param tests [Optional[Hash[Variant[String,Symbol],Any]]] Set of unit
285
+ # tests to apply to the matcher, expressed as `:method_call => value(s)`
286
+ # tuples. Use `nil` as the value for method_calls that don't accept
287
+ # arguments.
288
+ #
289
+ # @example With a particular provider (simple)
290
+ # matcher = RSpec::Puppet::MatcherHelpers.get_type_matcher(
291
+ # :be_valid_type,
292
+ # nil,
293
+ # :apt
294
+ # )
295
+ #
296
+ # @example With a particular provider (spelled out)
297
+ # matcher = RSpec::Puppet::MatcherHelpers.get_type_matcher(
298
+ # :be_valid_type,
299
+ # nil,
300
+ # { :with_provider => :apt }
301
+ # )
302
+ def self.get_type_matcher(method, args = [], tests = {})
303
+ matcher = RSpec::Puppet::TypeMatchers::CreateGeneric.new(
304
+ method,
305
+ args
306
+ )
307
+
308
+ if tests.kind_of?(Array)
309
+ tests.each { |test| matcher.send(test.to_sym) }
310
+ elsif tests.is_a?(Hash)
311
+ tests.each do |k,v|
312
+ if v.nil? || 'nil' == v.to_s
313
+ matcher.send(k.to_sym)
314
+ else
315
+ matcher.send(k.to_sym, v)
316
+ end
317
+ end
318
+ elsif !tests.nil?
319
+ # Anything left is assumed to be a 'with_provider' test
320
+ matcher.send(:with_provider, tests)
321
+ end
322
+
323
+ matcher
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,66 @@
1
+ # I was *forced* to copy the `guess_type_from_path` and `ref` functions from
2
+ # rspec-puppet/support (see the respective `see` references) and then slam
3
+ # everything into the global scope because I couldn't find a Ruby way to call
4
+ # these two function from my own namespaced code while keeping RSpec happy from
5
+ # the namespaced context. I tried a Class-based approach. I tried a
6
+ # Module-extend approach. Neither worked well. While I was able to make calls
7
+ # to these rspec-puppet functions from the appropriate scope in either model,
8
+ # doing so either broke RSpec's ability to find its own `it` or my namedspaced
9
+ # `apply_content` method (Class) or function (Module). I've now uterly burned
10
+ # over 30 hours of my life on this problem, so I'm "throwing in the towel" and
11
+ # just copying this code. I'm DONE fighting with Ruby on this issue! I'm sorry
12
+ # I wasn't smart enough to appease Ruby. Yes, it SUCKS to be blocked from using
13
+ # an OOP model or any namespaces but I just can't find any other way to move
14
+ # forward at this point... if YOU (yes, YOU) can refactor the necessary code
15
+ # into proper namespaces, then by all means; PLEASE feel free to open a Pull
16
+ # Request that that effect. I'd love to learn how you pull it off!
17
+ module RSpec::Puppet end
18
+
19
+ # Attempts to identify the nature of an entity-under-test from the path of the
20
+ # spec file that defines its unit tests.
21
+ #
22
+ # License: MIT
23
+ # @author Tim Sharpe
24
+ #
25
+ # @param path [String] Fully-qualified path to the *_spec.rb file that defines
26
+ # RSpec (Puppet) tests.
27
+ # @return [Symbol] Presumed nature of the entity-under-test.
28
+ #
29
+ # @see https://github.com/rodjek/rspec-puppet/blob/434653f8a143e047a082019975b66fb2323051eb/lib/rspec-puppet/support.rb#L25-L46
30
+ def guess_type_from_path(path)
31
+ case path
32
+ when /spec\/defines/
33
+ :define
34
+ when /spec\/classes/
35
+ :class
36
+ when /spec\/functions/
37
+ :function
38
+ when /spec\/hosts/
39
+ :host
40
+ when /spec\/types/
41
+ :type
42
+ when /spec\/type_aliases/
43
+ :type_alias
44
+ when /spec\/provider/
45
+ :provider
46
+ when /spec\/applications/
47
+ :application
48
+ else
49
+ :unknown
50
+ end
51
+ end
52
+
53
+ # Helper to return a resource/node reference, so it gets translated in params to a raw string
54
+ # without quotes.
55
+ #
56
+ # License: MIT
57
+ # @author Tim Sharpe
58
+ #
59
+ # @param [String] type reference type
60
+ # @param [String] title reference title
61
+ # @return [RSpec::Puppet::RawString] return a new RawString with the type/title populated correctly
62
+ #
63
+ # @see https://github.com/rodjek/rspec-puppet/blob/434653f8a143e047a082019975b66fb2323051eb/lib/rspec-puppet/support.rb#L404-L412
64
+ def ref(type, title)
65
+ return RSpec::Puppet::RawString.new("#{type}['#{title}']")
66
+ end
@@ -0,0 +1,176 @@
1
+ module RSpec::Puppet
2
+ module Yaml
3
+ # A collection of static methods that help coerce data into simpler (to
4
+ # digest) forms. This is necessitated primarily because YAML files can be
5
+ # written with either String or Symbol keys for the same value and because
6
+ # Hashes can be explicitly or implicitly named.
7
+ module DataHelpers
8
+ # Takes a Hash, checks it for a name attribute, and ensures that name
9
+ # is a string-named, 'name', attribute. If the Hash has no identifyable
10
+ # name, an ArgumentError is raised.
11
+ #
12
+ # @param transform_hash [Hash] A Hash that may already have an explicit
13
+ # name attribute in either string or symbol form, or a Hash with an
14
+ # implicit name of form `{ 'implicit' => { key... => val... } }`.
15
+ # @return Hash The `transform_hash` with an explicit, string key 'name'
16
+ # attribute.
17
+ # @raise ArgumentError when `transform_hash` has neither an implicit nor
18
+ # explicit name.
19
+ def self.make_hash_name_explicit(transform_hash = {})
20
+ raise ArgumentError, "Cannot transform non-Hash data." unless transform_hash.is_a?(Hash)
21
+ transformed_hash = {}
22
+ hash_name = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
23
+ 'name',
24
+ transform_hash
25
+ )
26
+
27
+ if hash_name.nil? || hash_name.empty?
28
+ # Could be an implicit name when { 'implicit' => { 'key' => 'val' } }
29
+ if 1 == transform_hash.keys.count
30
+ first_value = transform_hash.values[0]
31
+ if !first_value.nil? && !first_value.is_a?(Hash)
32
+ raise ArgumentError, "Cannot transform implicitly named Hash when its value is neither nil nor a Hash."
33
+ else
34
+ hash_name = transform_hash.keys.first
35
+ hash_value = first_value ||= {}
36
+ transformed_hash = hash_value
37
+ .select {|k,v| k != :name}
38
+ .merge({'name' => hash_name})
39
+ end
40
+ else
41
+ # No explicit or implicit name
42
+ raise ArgumentError, "Hash has neither an explicit nor implicit name."
43
+ end
44
+ else
45
+ transformed_hash = transform_hash
46
+ .select {|k,v| k != :name}
47
+ .merge({'name' => hash_name})
48
+ end
49
+
50
+ transformed_hash
51
+ end
52
+
53
+ # Condenses a collection of named Hashes into an Array-of-Hashes.
54
+ #
55
+ # Accepts Hashes-of-Hashes -- where each key is the entry's :name and --
56
+ # Arrays-of-Hashes -- where each Hash has a :name element -- returning
57
+ # both forms as an Array-of-Hashes with a guaranteed 'name' element. Any
58
+ # entry without a :name or 'name' generates an exception.
59
+ #
60
+ # @param [Enum[String,Symbol]] key The name of the collection to copy.
61
+ # @param [Optional[Hash]] data The Hash to copy `key` from.
62
+ # @return [Array[Hash]] Array-of-Hashes, each with a 'name' attribute.
63
+ # @raise [ArgumentError] when an element has no name or is not a Hash.
64
+ def self.get_array_of_named_hashes(key, data = {})
65
+ coerced_hashes = []
66
+ hashes = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
67
+ key,
68
+ data,
69
+ {}
70
+ )
71
+ return coerced_hashes if hashes.empty?
72
+
73
+ # Supported Cases:
74
+ # 1. [{'this is a name' => {:key => val}}, {'this is also a name' => {:key => val}}]
75
+ # 2. {'this is a name' => {:key => value}, 'this is also a name' => {:key => value}}
76
+ # 3. [{:name => 'the name', :key => value}, {:name => 'another name', :key => value}]
77
+ # 4. {:name => 'the name', :key => value}
78
+ if hashes.kind_of?(Array)
79
+ hashes.each { |hash|
80
+ coerced_hashes << RSpec::Puppet::Yaml::DataHelpers
81
+ .make_hash_name_explicit(hash)
82
+ }
83
+ elsif hashes.is_a?(Hash)
84
+ # Supported Case 2 requires that each Hash attribute be a named Hash
85
+ # when there is no explicit name attribute.
86
+ if hashes.has_key?('name') || hashes.has_key?(:name)
87
+ coerced_hashes << RSpec::Puppet::Yaml::DataHelpers
88
+ .make_hash_name_explicit(hashes)
89
+ else
90
+ hashes.each do |k, v|
91
+ if v.nil?
92
+ coerced_hashes << { 'name' => k }
93
+ elsif !v.is_a?(Hash)
94
+ raise ArgumentError, "#{key} indicates a Hash but at least one of its attributes is neither a Hash nor nil."
95
+ else
96
+ coerced_hashes << v
97
+ .select {|m, n| m != :name}
98
+ .merge({'name' => k})
99
+ end
100
+ end
101
+ end
102
+ else
103
+ raise ArgumentError, "#{key} is for neither an Array nor a Hash value."
104
+ end
105
+
106
+ coerced_hashes
107
+ end
108
+
109
+ # Gets a named Hash child from a supplied Hash parent.
110
+ #
111
+ # Attempts to get an immediate child Hash from a parent Hash that is
112
+ # named according to a given key. The key may be supplied in either
113
+ # String or Symbol form and both are searched for. When both forms
114
+ # exist in the parent Hash, a shallow merge is attempted, favoring the
115
+ # String form.
116
+ #
117
+ # @param [Enum[String,Symbol]] key The name of the child Hash.
118
+ # @param [Optional[Hash]] data The parent Hash.
119
+ # @param [Optional[Hash]] default The result when `key` not found.
120
+ # @return [Hash] The selected child Hash if it exists, else `default`.
121
+ # @raise NameError when the value for `key` is not a Hash.
122
+ def self.get_named_hash(key, data = {}, default = {})
123
+ hash_value = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
124
+ key, data, default
125
+ )
126
+ if !hash_value.is_a?(Hash)
127
+ raise NameError, "The value of #{key} is not a Hash."
128
+ end
129
+ hash_value
130
+ end
131
+
132
+ # Gets a named value from a Hash by its String and Symbol names.
133
+ #
134
+ # Searches a Hash for a key by both its String and Symbol names. When
135
+ # both are found, they are merged as long as their values are both Hash
136
+ # (shallow) or Array (unique). An exception is raised when the values are
137
+ # scalar or of different data types.
138
+ #
139
+ # @param [Enum[String,Symbol]] key The name of the child.
140
+ # @param [Optional[Hash]] data The parent Hash.
141
+ # @param [Optional[Hash]] default The result when `key` not found.
142
+ # @return [Any] The selected value if it exists, else `default`
143
+ # @raise NameError when both String and Symbol forms `key` exist but one
144
+ # cannot be merged into the other.
145
+ def self.get_named_value(key, data = {}, default = nil)
146
+ return default unless !data.nil? && data.respond_to?(:has_key?)
147
+ str_key = key.to_s
148
+ sym_key = key.to_sym
149
+ has_str_key = data.has_key?(str_key)
150
+ has_sym_key = data.has_key?(sym_key)
151
+
152
+ if has_str_key && has_sym_key
153
+ str_value = data[str_key]
154
+ sym_value = data[sym_key]
155
+ if str_value.is_a?(Hash) && sym_value.is_a?(Hash)
156
+ begin
157
+ sym_value.merge(str_value)
158
+ rescue
159
+ raise NameError, "Both symbolic and string forms of '#{str_key}' Hash declarations exist but one cannot be merged into the other. Pick one form or the other or ensure both are Hashes that can be merged."
160
+ end
161
+ elsif str_value.kind_of?(Array) && sym_value.kind_of?(Array)
162
+ str_value | sym_value
163
+ else
164
+ raise NameError, "Both symbolic and string forms of '#{str_key}' scalar declarations exist and they cannot be combined. Pick one form or the other."
165
+ end
166
+ elsif has_str_key
167
+ data[str_key]
168
+ elsif has_sym_key
169
+ data[sym_key]
170
+ else
171
+ default
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,28 @@
1
+ # Identifies a YAML data file based on the name of a *_spec.rb rspec file and
2
+ # passes it to `parse_rspec_puppet_yaml` to initiate parsing.
3
+ #
4
+ # @param rspec_file Path to and name of the RSpec *_spec.rb file. It is easiest
5
+ # to simply pass `__FILE__` to this function from that file.
6
+ #
7
+ # @example Typical use
8
+ # require 'spec_helper'
9
+ # parse_yaml_from_spec(__FILE__)
10
+ #
11
+ # @since 0.1.0
12
+ def parse_yaml_from_spec(rspec_file)
13
+ if rspec_file =~ /^(.+)_spec\.rb$/
14
+ [ "#{$1}_spec.yaml",
15
+ "#{$1}_spec.yml",
16
+ "#{$1}.yaml",
17
+ "#{$1}.yml"
18
+ ].each do |yaml_file|
19
+ if File.exist?(yaml_file)
20
+ parse_rspec_puppet_yaml(yaml_file)
21
+ end
22
+ end
23
+ elsif rspec_file =~ /^(.+\.ya?ml)$/
24
+ parse_rspec_puppet_yaml($1)
25
+ else
26
+ parse_rspec_puppet_yaml(rspec_file)
27
+ end
28
+ end