rspec-puppet-yaml 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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