rspec-puppet-yaml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.inch.yml +4 -0
- data/.rspec +2 -0
- data/.travis.yml +29 -0
- data/.yardopts +2 -0
- data/CODE_OF_CONDUCT.md +75 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +93 -0
- data/LICENSE.txt +21 -0
- data/README.md +759 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rspec-puppet/matcher_helpers.rb +326 -0
- data/lib/rspec-puppet/support_copy.rb +66 -0
- data/lib/rspec-puppet-yaml/data_helpers.rb +176 -0
- data/lib/rspec-puppet-yaml/extenders.rb +28 -0
- data/lib/rspec-puppet-yaml/parser.rb +512 -0
- data/lib/rspec-puppet-yaml/version.rb +11 -0
- data/lib/rspec-puppet-yaml.rb +8 -0
- data/rspec-puppet-yaml.gemspec +45 -0
- metadata +208 -0
data/Rakefile
ADDED
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,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
|