omnijack 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 +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
|
+
|