omnijack 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 +15 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +15 -0
- data/NOTICE +5 -0
- data/README.md +119 -0
- data/Rakefile +23 -0
- data/features/list.feature +19 -0
- data/features/metadata.feature +43 -0
- data/features/platforms.feature +23 -0
- data/features/step_definitions/list.rb +12 -0
- data/features/step_definitions/metadata.rb +20 -0
- data/features/step_definitions/platforms.rb +8 -0
- data/features/step_definitions/project.rb +20 -0
- data/features/support/env.rb +4 -0
- data/lib/omnijack/config.rb +67 -0
- data/lib/omnijack/list.rb +94 -0
- data/lib/omnijack/metadata.rb +244 -0
- data/lib/omnijack/platforms.rb +96 -0
- data/lib/omnijack/project/metaprojects.rb +38 -0
- data/lib/omnijack/project.rb +63 -0
- data/lib/omnijack/version.rb +24 -0
- data/lib/omnijack.rb +54 -0
- data/omnijack.gemspec +37 -0
- data/spec/omnijack/config_spec.rb +55 -0
- data/spec/omnijack/list_spec.rb +133 -0
- data/spec/omnijack/metadata_spec.rb +577 -0
- data/spec/omnijack/platforms_spec.rb +132 -0
- data/spec/omnijack/project/angry_chef_spec.rb +55 -0
- data/spec/omnijack/project/chef_container_spec.rb +55 -0
- data/spec/omnijack/project/chef_dk_spec.rb +55 -0
- data/spec/omnijack/project/chef_server_spec.rb +55 -0
- data/spec/omnijack/project/chef_spec.rb +55 -0
- data/spec/omnijack/project_spec.rb +52 -0
- data/spec/omnijack_spec.rb +109 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/real_test_data.json +131 -0
- data/vendor/chef/LICENSE +201 -0
- data/vendor/chef/NOTICE +21 -0
- data/vendor/chef/lib/chef/exceptions.rb +353 -0
- data/vendor/chef/lib/chef/mixin/params_validate.rb +242 -0
- metadata +276 -0
@@ -0,0 +1,353 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Author:: Seth Falcon (<seth@opscode.com>)
|
4
|
+
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
|
5
|
+
# Copyright:: Copyright 2008-2010 Opscode, Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
|
20
|
+
class Chef
|
21
|
+
# == Chef::Exceptions
|
22
|
+
# Chef's custom exceptions are all contained within the Chef::Exceptions
|
23
|
+
# namespace.
|
24
|
+
class Exceptions
|
25
|
+
|
26
|
+
# Backcompat with Chef::ShellOut code:
|
27
|
+
require 'mixlib/shellout/exceptions'
|
28
|
+
|
29
|
+
def self.const_missing(const_name)
|
30
|
+
if const_name == :ShellCommandFailed
|
31
|
+
Chef::Log.warn("Chef::Exceptions::ShellCommandFailed is deprecated, use Mixlib::ShellOut::ShellCommandFailed")
|
32
|
+
called_from = caller[0..3].inject("Called from:\n") {|msg, trace_line| msg << " #{trace_line}\n" }
|
33
|
+
Chef::Log.warn(called_from)
|
34
|
+
Mixlib::ShellOut::ShellCommandFailed
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Application < RuntimeError; end
|
41
|
+
class Cron < RuntimeError; end
|
42
|
+
class Env < RuntimeError; end
|
43
|
+
class Exec < RuntimeError; end
|
44
|
+
class ErlCall < RuntimeError; end
|
45
|
+
class FileNotFound < RuntimeError; end
|
46
|
+
class Package < RuntimeError; end
|
47
|
+
class Service < RuntimeError; end
|
48
|
+
class Route < RuntimeError; end
|
49
|
+
class SearchIndex < RuntimeError; end
|
50
|
+
class Override < RuntimeError; end
|
51
|
+
class UnsupportedAction < RuntimeError; end
|
52
|
+
class MissingLibrary < RuntimeError; end
|
53
|
+
|
54
|
+
class CannotDetermineNodeName < RuntimeError
|
55
|
+
def initialize
|
56
|
+
super "Unable to determine node name: configure node_name or configure the system's hostname and fqdn"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class User < RuntimeError; end
|
61
|
+
class Group < RuntimeError; end
|
62
|
+
class Link < RuntimeError; end
|
63
|
+
class Mount < RuntimeError; end
|
64
|
+
class PrivateKeyMissing < RuntimeError; end
|
65
|
+
class CannotWritePrivateKey < RuntimeError; end
|
66
|
+
class RoleNotFound < RuntimeError; end
|
67
|
+
class DuplicateRole < RuntimeError; end
|
68
|
+
class ValidationFailed < ArgumentError; end
|
69
|
+
class InvalidPrivateKey < ArgumentError; end
|
70
|
+
class ConfigurationError < ArgumentError; end
|
71
|
+
class RedirectLimitExceeded < RuntimeError; end
|
72
|
+
class AmbiguousRunlistSpecification < ArgumentError; end
|
73
|
+
class CookbookFrozen < ArgumentError; end
|
74
|
+
class CookbookNotFound < RuntimeError; end
|
75
|
+
# Cookbook loader used to raise an argument error when cookbook not found.
|
76
|
+
# for back compat, need to raise an error that inherits from ArgumentError
|
77
|
+
class CookbookNotFoundInRepo < ArgumentError; end
|
78
|
+
class RecipeNotFound < ArgumentError; end
|
79
|
+
class AttributeNotFound < RuntimeError; end
|
80
|
+
class MissingCookbookDependency < StandardError; end # CHEF-5120
|
81
|
+
class InvalidCommandOption < RuntimeError; end
|
82
|
+
class CommandTimeout < RuntimeError; end
|
83
|
+
class RequestedUIDUnavailable < RuntimeError; end
|
84
|
+
class InvalidHomeDirectory < ArgumentError; end
|
85
|
+
class DsclCommandFailed < RuntimeError; end
|
86
|
+
class PlistUtilCommandFailed < RuntimeError; end
|
87
|
+
class UserIDNotFound < ArgumentError; end
|
88
|
+
class GroupIDNotFound < ArgumentError; end
|
89
|
+
class ConflictingMembersInGroup < ArgumentError; end
|
90
|
+
class InvalidResourceReference < RuntimeError; end
|
91
|
+
class ResourceNotFound < RuntimeError; end
|
92
|
+
|
93
|
+
# Can't find a Resource of this type that is valid on this platform.
|
94
|
+
class NoSuchResourceType < NameError; end
|
95
|
+
|
96
|
+
class InvalidResourceSpecification < ArgumentError; end
|
97
|
+
class SolrConnectionError < RuntimeError; end
|
98
|
+
class IllegalChecksumRevert < RuntimeError; end
|
99
|
+
class CookbookVersionNameMismatch < ArgumentError; end
|
100
|
+
class MissingParentDirectory < RuntimeError; end
|
101
|
+
class UnresolvableGitReference < RuntimeError; end
|
102
|
+
class InvalidRemoteGitReference < RuntimeError; end
|
103
|
+
class InvalidEnvironmentRunListSpecification < ArgumentError; end
|
104
|
+
class InvalidDataBagItemID < ArgumentError; end
|
105
|
+
class InvalidDataBagName < ArgumentError; end
|
106
|
+
class EnclosingDirectoryDoesNotExist < ArgumentError; end
|
107
|
+
# Errors originating from calls to the Win32 API
|
108
|
+
class Win32APIError < RuntimeError; end
|
109
|
+
# Thrown when Win32 API layer binds to non-existent Win32 function. Occurs
|
110
|
+
# when older versions of Windows don't support newer Win32 API functions.
|
111
|
+
class Win32APIFunctionNotImplemented < NotImplementedError; end
|
112
|
+
# Attempting to run windows code on a not-windows node
|
113
|
+
class Win32NotWindows < RuntimeError; end
|
114
|
+
class WindowsNotAdmin < RuntimeError; end
|
115
|
+
# Attempting to access a 64-bit only resource on a 32-bit Windows system
|
116
|
+
class Win32ArchitectureIncorrect < RuntimeError; end
|
117
|
+
class ObsoleteDependencySyntax < ArgumentError; end
|
118
|
+
class InvalidDataBagPath < ArgumentError; end
|
119
|
+
class DuplicateDataBagItem < RuntimeError; end
|
120
|
+
|
121
|
+
# A different version of a cookbook was added to a
|
122
|
+
# VersionedRecipeList than the one already there.
|
123
|
+
class CookbookVersionConflict < ArgumentError ; end
|
124
|
+
|
125
|
+
# does not follow X.Y.Z format. ArgumentError?
|
126
|
+
class InvalidPlatformVersion < ArgumentError; end
|
127
|
+
class InvalidCookbookVersion < ArgumentError; end
|
128
|
+
|
129
|
+
# version constraint should be a string or array, or it doesn't
|
130
|
+
# match OP VERSION. ArgumentError?
|
131
|
+
class InvalidVersionConstraint < ArgumentError; end
|
132
|
+
|
133
|
+
# Version constraints are not allowed in chef-solo
|
134
|
+
class IllegalVersionConstraint < NotImplementedError; end
|
135
|
+
|
136
|
+
class MetadataNotValid < StandardError; end
|
137
|
+
|
138
|
+
# File operation attempted but no permissions to perform it
|
139
|
+
class InsufficientPermissions < RuntimeError; end
|
140
|
+
|
141
|
+
# Ifconfig failed
|
142
|
+
class Ifconfig < RuntimeError; end
|
143
|
+
|
144
|
+
# Invalid "source" parameter to a remote_file resource
|
145
|
+
class InvalidRemoteFileURI < ArgumentError; end
|
146
|
+
|
147
|
+
# Node::Attribute computes the merged version of of attributes
|
148
|
+
# and makes it read-only. Attempting to modify a read-only
|
149
|
+
# attribute will cause this error.
|
150
|
+
class ImmutableAttributeModification < NoMethodError; end
|
151
|
+
|
152
|
+
# Merged node attributes are invalidated when the component
|
153
|
+
# attributes are updated. Attempting to read from a stale copy
|
154
|
+
# of merged attributes will trigger this error.
|
155
|
+
class StaleAttributeRead < StandardError; end
|
156
|
+
|
157
|
+
# Registry Helper throws the following errors
|
158
|
+
class Win32RegArchitectureIncorrect < Win32ArchitectureIncorrect; end
|
159
|
+
class Win32RegHiveMissing < ArgumentError; end
|
160
|
+
class Win32RegKeyMissing < RuntimeError; end
|
161
|
+
class Win32RegValueMissing < RuntimeError; end
|
162
|
+
class Win32RegDataMissing < RuntimeError; end
|
163
|
+
class Win32RegValueExists < RuntimeError; end
|
164
|
+
class Win32RegNoRecursive < ArgumentError; end
|
165
|
+
class Win32RegTypeDoesNotExist < ArgumentError; end
|
166
|
+
class Win32RegBadType < ArgumentError; end
|
167
|
+
class Win32RegBadValueSize < ArgumentError; end
|
168
|
+
class Win32RegTypesMismatch < ArgumentError; end
|
169
|
+
|
170
|
+
class InvalidEnvironmentPath < ArgumentError; end
|
171
|
+
class EnvironmentNotFound < RuntimeError; end
|
172
|
+
|
173
|
+
# File-like resource found a non-file (socket, pipe, directory, etc) at its destination
|
174
|
+
class FileTypeMismatch < RuntimeError; end
|
175
|
+
|
176
|
+
# File (or descendent) resource configured to manage symlink source, but
|
177
|
+
# the symlink that is there either loops or points to a nonexistent file
|
178
|
+
class InvalidSymlink < RuntimeError; end
|
179
|
+
|
180
|
+
class ChildConvergeError < RuntimeError; end
|
181
|
+
|
182
|
+
class MissingRole < RuntimeError
|
183
|
+
NULL = Object.new
|
184
|
+
|
185
|
+
attr_reader :expansion
|
186
|
+
|
187
|
+
def initialize(message_or_expansion=NULL)
|
188
|
+
@expansion = nil
|
189
|
+
case message_or_expansion
|
190
|
+
when NULL
|
191
|
+
super()
|
192
|
+
when String
|
193
|
+
super
|
194
|
+
when RunList::RunListExpansion
|
195
|
+
@expansion = message_or_expansion
|
196
|
+
missing_roles = @expansion.errors.join(', ')
|
197
|
+
super("The expanded run list includes nonexistent roles: #{missing_roles}")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
# Exception class for collecting multiple failures. Used when running
|
203
|
+
# delayed notifications so that chef can process each delayed
|
204
|
+
# notification even if chef client or other notifications fail.
|
205
|
+
class MultipleFailures < StandardError
|
206
|
+
def initialize(*args)
|
207
|
+
super
|
208
|
+
@all_failures = []
|
209
|
+
end
|
210
|
+
|
211
|
+
def message
|
212
|
+
base = "Multiple failures occurred:\n"
|
213
|
+
@all_failures.inject(base) do |message, (location, error)|
|
214
|
+
message << "* #{error.class} occurred in #{location}: #{error.message}\n"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def client_run_failure(exception)
|
219
|
+
set_backtrace(exception.backtrace)
|
220
|
+
@all_failures << [ "chef run", exception ]
|
221
|
+
end
|
222
|
+
|
223
|
+
def notification_failure(exception)
|
224
|
+
@all_failures << [ "delayed notification", exception ]
|
225
|
+
end
|
226
|
+
|
227
|
+
def raise!
|
228
|
+
unless empty?
|
229
|
+
raise self.for_raise
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def empty?
|
234
|
+
@all_failures.empty?
|
235
|
+
end
|
236
|
+
|
237
|
+
def for_raise
|
238
|
+
if @all_failures.size == 1
|
239
|
+
@all_failures[0][1]
|
240
|
+
else
|
241
|
+
self
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
class CookbookVersionSelection
|
247
|
+
|
248
|
+
# Compound exception: In run_list expansion and resolution,
|
249
|
+
# run_list items referred to cookbooks that don't exist and/or
|
250
|
+
# have no versions available.
|
251
|
+
class InvalidRunListItems < StandardError
|
252
|
+
attr_reader :non_existent_cookbooks
|
253
|
+
attr_reader :cookbooks_with_no_matching_versions
|
254
|
+
|
255
|
+
def initialize(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
|
256
|
+
super(message)
|
257
|
+
|
258
|
+
@non_existent_cookbooks = non_existent_cookbooks
|
259
|
+
@cookbooks_with_no_matching_versions = cookbooks_with_no_matching_versions
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_json(*a)
|
263
|
+
result = {
|
264
|
+
"message" => message,
|
265
|
+
"non_existent_cookbooks" => non_existent_cookbooks,
|
266
|
+
"cookbooks_with_no_versions" => cookbooks_with_no_matching_versions
|
267
|
+
}
|
268
|
+
Chef::JSONCompat.to_json(result, *a)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# In run_list expansion and resolution, a constraint was
|
273
|
+
# unsatisfiable.
|
274
|
+
#
|
275
|
+
# This exception may not be the complete error report. If you
|
276
|
+
# resolve the misconfiguration represented by this exception and
|
277
|
+
# re-solve, you may get another exception
|
278
|
+
class UnsatisfiableRunListItem < StandardError
|
279
|
+
attr_reader :run_list_item
|
280
|
+
attr_reader :non_existent_cookbooks, :most_constrained_cookbooks
|
281
|
+
|
282
|
+
# most_constrained_cookbooks: if I were to remove constraints
|
283
|
+
# regarding these cookbooks, I would get a solution or move on
|
284
|
+
# to the next error (deeper in the graph). An item in this list
|
285
|
+
# may be unsatisfiable, but when resolved may also reveal
|
286
|
+
# further unsatisfiable constraints; this condition would not be
|
287
|
+
# reported.
|
288
|
+
def initialize(message, run_list_item, non_existent_cookbooks, most_constrained_cookbooks)
|
289
|
+
super(message)
|
290
|
+
|
291
|
+
@run_list_item = run_list_item
|
292
|
+
@non_existent_cookbooks = non_existent_cookbooks
|
293
|
+
@most_constrained_cookbooks = most_constrained_cookbooks
|
294
|
+
end
|
295
|
+
|
296
|
+
def to_json(*a)
|
297
|
+
result = {
|
298
|
+
"message" => message,
|
299
|
+
"unsatisfiable_run_list_item" => run_list_item,
|
300
|
+
"non_existent_cookbooks" => non_existent_cookbooks,
|
301
|
+
"most_constrained_cookbooks" => most_constrained_cookbooks
|
302
|
+
}
|
303
|
+
Chef::JSONCompat.to_json(result, *a)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
end # CookbookVersionSelection
|
308
|
+
|
309
|
+
# When the server sends a redirect, RFC 2616 states a user-agent should
|
310
|
+
# not follow it with a method other than GET or HEAD, unless a specific
|
311
|
+
# action is taken by the user. A redirect received as response to a
|
312
|
+
# non-GET and non-HEAD request will thus raise an InvalidRedirect.
|
313
|
+
class InvalidRedirect < StandardError; end
|
314
|
+
|
315
|
+
# Raised when the content length of a download does not match the content
|
316
|
+
# length declared in the http response.
|
317
|
+
class ContentLengthMismatch < RuntimeError
|
318
|
+
def initialize(response_length, content_length)
|
319
|
+
super "Response body length #{response_length} does not match HTTP Content-Length header #{content_length}."
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
class UnsupportedPlatform < RuntimeError
|
324
|
+
def initialize(platform)
|
325
|
+
super "This functionality is not supported on platform #{platform}."
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Raised when Chef::Config[:run_lock_timeout] is set and some other client run fails
|
330
|
+
# to release the run lock becure Chef::Config[:run_lock_timeout] seconds pass.
|
331
|
+
class RunLockTimeout < RuntimeError
|
332
|
+
def initialize(duration, blocking_pid)
|
333
|
+
super "Unable to acquire lock. Waited #{duration} seconds for #{blocking_pid} to release."
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
class ChecksumMismatch < RuntimeError
|
338
|
+
def initialize(res_cksum, cont_cksum)
|
339
|
+
super "Checksum on resource (#{res_cksum}) does not match checksum on content (#{cont_cksum})"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
class BadProxyURI < RuntimeError; end
|
344
|
+
|
345
|
+
# Raised by Chef::JSONCompat
|
346
|
+
class JSON
|
347
|
+
class EncodeError < RuntimeError; end
|
348
|
+
class ParseError < RuntimeError; end
|
349
|
+
end
|
350
|
+
|
351
|
+
class InvalidSearchQuery < ArgumentError; end
|
352
|
+
end
|
353
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2008 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
class Chef
|
19
|
+
class DelayedEvaluator < Proc
|
20
|
+
end
|
21
|
+
module Mixin
|
22
|
+
module ParamsValidate
|
23
|
+
|
24
|
+
# Takes a hash of options, along with a map to validate them. Returns the original
|
25
|
+
# options hash, plus any changes that might have been made (through things like setting
|
26
|
+
# default values in the validation map)
|
27
|
+
#
|
28
|
+
# For example:
|
29
|
+
#
|
30
|
+
# validate({ :one => "neat" }, { :one => { :kind_of => String }})
|
31
|
+
#
|
32
|
+
# Would raise an exception if the value of :one above is not a kind_of? string. Valid
|
33
|
+
# map options are:
|
34
|
+
#
|
35
|
+
# :default:: Sets the default value for this parameter.
|
36
|
+
# :callbacks:: Takes a hash of Procs, which should return true if the argument is valid.
|
37
|
+
# The key will be inserted into the error message if the Proc does not return true:
|
38
|
+
# "Option #{key}'s value #{value} #{message}!"
|
39
|
+
# :kind_of:: Ensure that the value is a kind_of?(Whatever). If passed an array, it will ensure
|
40
|
+
# that the value is one of those types.
|
41
|
+
# :respond_to:: Ensure that the value has a given method. Takes one method name or an array of
|
42
|
+
# method names.
|
43
|
+
# :required:: Raise an exception if this parameter is missing. Valid values are true or false,
|
44
|
+
# by default, options are not required.
|
45
|
+
# :regex:: Match the value of the paramater against a regular expression.
|
46
|
+
# :equal_to:: Match the value of the paramater with ==. An array means it can be equal to any
|
47
|
+
# of the values.
|
48
|
+
def validate(opts, map)
|
49
|
+
#--
|
50
|
+
# validate works by taking the keys in the validation map, assuming it's a hash, and
|
51
|
+
# looking for _pv_:symbol as methods. Assuming it find them, it calls the right
|
52
|
+
# one.
|
53
|
+
#++
|
54
|
+
raise ArgumentError, "Options must be a hash" unless opts.kind_of?(Hash)
|
55
|
+
raise ArgumentError, "Validation Map must be a hash" unless map.kind_of?(Hash)
|
56
|
+
|
57
|
+
map.each do |key, validation|
|
58
|
+
unless key.kind_of?(Symbol) || key.kind_of?(String)
|
59
|
+
raise ArgumentError, "Validation map keys must be symbols or strings!"
|
60
|
+
end
|
61
|
+
case validation
|
62
|
+
when true
|
63
|
+
_pv_required(opts, key)
|
64
|
+
when false
|
65
|
+
true
|
66
|
+
when Hash
|
67
|
+
validation.each do |check, carg|
|
68
|
+
check_method = "_pv_#{check.to_s}"
|
69
|
+
if self.respond_to?(check_method, true)
|
70
|
+
self.send(check_method, opts, key, carg)
|
71
|
+
else
|
72
|
+
raise ArgumentError, "Validation map has unknown check: #{check}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
opts
|
78
|
+
end
|
79
|
+
|
80
|
+
def lazy(&block)
|
81
|
+
DelayedEvaluator.new(&block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_or_return(symbol, arg, validation)
|
85
|
+
iv_symbol = "@#{symbol.to_s}".to_sym
|
86
|
+
if arg == nil && self.instance_variable_defined?(iv_symbol) == true
|
87
|
+
ivar = self.instance_variable_get(iv_symbol)
|
88
|
+
if(ivar.is_a?(DelayedEvaluator))
|
89
|
+
validate({ symbol => ivar.call }, { symbol => validation })[symbol]
|
90
|
+
else
|
91
|
+
ivar
|
92
|
+
end
|
93
|
+
else
|
94
|
+
if(arg.is_a?(DelayedEvaluator))
|
95
|
+
val = arg
|
96
|
+
else
|
97
|
+
val = validate({ symbol => arg }, { symbol => validation })[symbol]
|
98
|
+
|
99
|
+
# Handle the case where the "default" was a DelayedEvaluator. In
|
100
|
+
# this case, the block yields an optional parameter of +self+,
|
101
|
+
# which is the equivalent of "new_resource"
|
102
|
+
if val.is_a?(DelayedEvaluator)
|
103
|
+
val = val.call(self)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
self.instance_variable_set(iv_symbol, val)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Return the value of a parameter, or nil if it doesn't exist.
|
113
|
+
def _pv_opts_lookup(opts, key)
|
114
|
+
if opts.has_key?(key.to_s)
|
115
|
+
opts[key.to_s]
|
116
|
+
elsif opts.has_key?(key.to_sym)
|
117
|
+
opts[key.to_sym]
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Raise an exception if the parameter is not found.
|
124
|
+
def _pv_required(opts, key, is_required=true)
|
125
|
+
if is_required
|
126
|
+
if (opts.has_key?(key.to_s) && !opts[key.to_s].nil?) ||
|
127
|
+
(opts.has_key?(key.to_sym) && !opts[key.to_sym].nil?)
|
128
|
+
true
|
129
|
+
else
|
130
|
+
raise Exceptions::ValidationFailed, "Required argument #{key} is missing!"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def _pv_equal_to(opts, key, to_be)
|
136
|
+
value = _pv_opts_lookup(opts, key)
|
137
|
+
unless value.nil?
|
138
|
+
passes = false
|
139
|
+
Array(to_be).each do |tb|
|
140
|
+
passes = true if value == tb
|
141
|
+
end
|
142
|
+
unless passes
|
143
|
+
raise Exceptions::ValidationFailed, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}."
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Raise an exception if the parameter is not a kind_of?(to_be)
|
149
|
+
def _pv_kind_of(opts, key, to_be)
|
150
|
+
value = _pv_opts_lookup(opts, key)
|
151
|
+
unless value.nil?
|
152
|
+
passes = false
|
153
|
+
Array(to_be).each do |tb|
|
154
|
+
passes = true if value.kind_of?(tb)
|
155
|
+
end
|
156
|
+
unless passes
|
157
|
+
raise Exceptions::ValidationFailed, "Option #{key} must be a kind of #{to_be}! You passed #{value.inspect}."
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Raise an exception if the parameter does not respond to a given set of methods.
|
163
|
+
def _pv_respond_to(opts, key, method_name_list)
|
164
|
+
value = _pv_opts_lookup(opts, key)
|
165
|
+
unless value.nil?
|
166
|
+
Array(method_name_list).each do |method_name|
|
167
|
+
unless value.respond_to?(method_name)
|
168
|
+
raise Exceptions::ValidationFailed, "Option #{key} must have a #{method_name} method!"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Assert that parameter returns false when passed a predicate method.
|
175
|
+
# For example, :cannot_be => :blank will raise a Exceptions::ValidationFailed
|
176
|
+
# error value.blank? returns a 'truthy' (not nil or false) value.
|
177
|
+
#
|
178
|
+
# Note, this will *PASS* if the object doesn't respond to the method.
|
179
|
+
# So, to make sure a value is not nil and not blank, you need to do
|
180
|
+
# both :cannot_be => :blank *and* :cannot_be => :nil (or :required => true)
|
181
|
+
def _pv_cannot_be(opts, key, predicate_method_base_name)
|
182
|
+
value = _pv_opts_lookup(opts, key)
|
183
|
+
predicate_method = (predicate_method_base_name.to_s + "?").to_sym
|
184
|
+
|
185
|
+
if value.respond_to?(predicate_method)
|
186
|
+
if value.send(predicate_method)
|
187
|
+
raise Exceptions::ValidationFailed, "Option #{key} cannot be #{predicate_method_base_name}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Assign a default value to a parameter.
|
193
|
+
def _pv_default(opts, key, default_value)
|
194
|
+
value = _pv_opts_lookup(opts, key)
|
195
|
+
if value == nil
|
196
|
+
opts[key] = default_value
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Check a parameter against a regular expression.
|
201
|
+
def _pv_regex(opts, key, regex)
|
202
|
+
value = _pv_opts_lookup(opts, key)
|
203
|
+
if value != nil
|
204
|
+
passes = false
|
205
|
+
[ regex ].flatten.each do |r|
|
206
|
+
if value != nil
|
207
|
+
if r.match(value.to_s)
|
208
|
+
passes = true
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
unless passes
|
213
|
+
raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} does not match regular expression #{regex.inspect}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Check a parameter against a hash of proc's.
|
219
|
+
def _pv_callbacks(opts, key, callbacks)
|
220
|
+
raise ArgumentError, "Callback list must be a hash!" unless callbacks.kind_of?(Hash)
|
221
|
+
value = _pv_opts_lookup(opts, key)
|
222
|
+
if value != nil
|
223
|
+
callbacks.each do |message, zeproc|
|
224
|
+
if zeproc.call(value) != true
|
225
|
+
raise Exceptions::ValidationFailed, "Option #{key}'s value #{value} #{message}!"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Allow a parameter to default to @name
|
232
|
+
def _pv_name_attribute(opts, key, is_name_attribute=true)
|
233
|
+
if is_name_attribute
|
234
|
+
if opts[key] == nil
|
235
|
+
opts[key] = self.instance_variable_get("@name")
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|