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.
- 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
|