sensu-settings 0.0.1
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 +22 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +28 -0
- data/Rakefile +6 -0
- data/lib/sensu/settings.rb +15 -0
- data/lib/sensu/settings/constants.rb +5 -0
- data/lib/sensu/settings/loader.rb +255 -0
- data/lib/sensu/settings/rules.rb +154 -0
- data/lib/sensu/settings/validator.rb +76 -0
- data/lib/sensu/settings/validators.rb +23 -0
- data/lib/sensu/settings/validators/api.rb +36 -0
- data/lib/sensu/settings/validators/check.rb +78 -0
- data/lib/sensu/settings/validators/client.rb +113 -0
- data/lib/sensu/settings/validators/filter.rb +18 -0
- data/lib/sensu/settings/validators/handler.rb +133 -0
- data/lib/sensu/settings/validators/mutator.rb +18 -0
- data/lib/sensu/settings/validators/subdue.rb +68 -0
- data/lib/sensu/settings/validators/transport.rb +20 -0
- data/sensu-settings.gemspec +24 -0
- data/spec/assets/conf.d/merger.json +11 -0
- data/spec/assets/conf.d/nested/file.json +11 -0
- data/spec/assets/config.json +126 -0
- data/spec/assets/invalid.json +2 -0
- data/spec/helpers.rb +8 -0
- data/spec/loader_spec.rb +171 -0
- data/spec/rules_spec.rb +65 -0
- data/spec/settings_spec.rb +13 -0
- data/spec/validator_spec.rb +619 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b95af032c4b3a94e59a491db3c04ba24eb8379b0
|
4
|
+
data.tar.gz: 2e86d492aa3f2f2e8cfb55e078e351a4812a3df6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 54a52ab2912061d6eca52dc9f4dbfc84c0532cf9b4593275e653272fdaeb62e2989db94783a120eeb1b08c2630cd8567f261b6dec506c55636a4cffb932a5c72
|
7
|
+
data.tar.gz: b4410e0f99ce6cba6d820ef90e7503147a794001c20fcd476f8ef94cc04169b38bf9e19aa3af504d8dbc0a527d14bcdf70310e191c590cfd552e9303f26ff5b2
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Heavy Water Operations, LLC.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Sensu::Settings
|
2
|
+
|
3
|
+
[](https://travis-ci.org/sensu/sensu-settings)
|
4
|
+
|
5
|
+
[](https://codeclimate.com/github/sensu/sensu-settings)
|
6
|
+
[](https://codeclimate.com/github/sensu/sensu-settings)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'sensu-settings'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Documentation can be found [here](http://rubydoc.info/github/sensu/sensu-settings/Sensu/Settings).
|
21
|
+
|
22
|
+
## Contributing
|
23
|
+
|
24
|
+
1. [Fork it](https://github.com/sensu/sensu-settings/fork)
|
25
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
26
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
27
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
28
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "sensu/settings/loader"
|
2
|
+
|
3
|
+
module Sensu
|
4
|
+
module Settings
|
5
|
+
# Load Sensu settings.
|
6
|
+
#
|
7
|
+
# @param options [Hash]
|
8
|
+
# @return [Loader] a loaded instance of Loader.
|
9
|
+
def self.load(options={})
|
10
|
+
loader = Loader.new
|
11
|
+
loader.load(options)
|
12
|
+
loader
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
gem "multi_json", "1.10.0"
|
2
|
+
|
3
|
+
require "sensu/settings/validator"
|
4
|
+
require "multi_json"
|
5
|
+
|
6
|
+
module Sensu
|
7
|
+
module Settings
|
8
|
+
class Loader
|
9
|
+
# @!attribute [r] warnings
|
10
|
+
# @return [Array] loader warnings.
|
11
|
+
attr_reader :warnings
|
12
|
+
|
13
|
+
# @!attribute [r] loaded_files
|
14
|
+
# @return [Array] loaded config files.
|
15
|
+
attr_reader :loaded_files
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@warnings = []
|
19
|
+
@settings = Hash[CATEGORIES.map {|category| [category, {}]}]
|
20
|
+
@indifferent_access = false
|
21
|
+
@loaded_files = []
|
22
|
+
self.class.create_category_methods
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create setting category accessors and methods to test the
|
26
|
+
# existence of definitions. Called in initialize().
|
27
|
+
def self.create_category_methods
|
28
|
+
CATEGORIES.each do |category|
|
29
|
+
define_method(category) do
|
30
|
+
setting_category(category)
|
31
|
+
end
|
32
|
+
method_name = category.to_s.chop + "_exists?"
|
33
|
+
define_method(method_name.to_sym) do |name|
|
34
|
+
definition_exists?(category, name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Access settings as an indifferent hash.
|
40
|
+
#
|
41
|
+
# @return [Hash] settings.
|
42
|
+
def to_hash
|
43
|
+
unless @indifferent_access
|
44
|
+
indifferent_access!
|
45
|
+
end
|
46
|
+
@settings
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieve the value object corresponding to a key, acting like
|
50
|
+
# a Hash object.
|
51
|
+
#
|
52
|
+
# @param key [String, Symbol]
|
53
|
+
# @return [Object] value for key.
|
54
|
+
def [](key)
|
55
|
+
to_hash[key]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Load settings from the environment.
|
59
|
+
# Loads: RABBITMQ_URL, REDIS_URL, REDISTOGO_URL, API_PORT, PORT
|
60
|
+
def load_env
|
61
|
+
if ENV["RABBITMQ_URL"]
|
62
|
+
@settings[:rabbitmq] = ENV["RABBITMQ_URL"]
|
63
|
+
warning(@settings[:rabbitmq], "using rabbitmq url environment variable")
|
64
|
+
end
|
65
|
+
ENV["REDIS_URL"] ||= ENV["REDISTOGO_URL"]
|
66
|
+
if ENV["REDIS_URL"]
|
67
|
+
@settings[:redis] = ENV["REDIS_URL"]
|
68
|
+
warning(@settings[:redis], "using redis url environment variable")
|
69
|
+
end
|
70
|
+
ENV["API_PORT"] ||= ENV["PORT"]
|
71
|
+
if ENV["API_PORT"]
|
72
|
+
@settings[:api] ||= {}
|
73
|
+
@settings[:api][:port] = ENV["API_PORT"].to_i
|
74
|
+
warning(@settings[:api], "using api port environment variable")
|
75
|
+
end
|
76
|
+
@indifferent_access = false
|
77
|
+
end
|
78
|
+
|
79
|
+
# Load settings from a JSON file.
|
80
|
+
#
|
81
|
+
# @param [String] file path.
|
82
|
+
def load_file(file)
|
83
|
+
if File.file?(file) && File.readable?(file)
|
84
|
+
begin
|
85
|
+
warning(file, "loading config file")
|
86
|
+
contents = IO.read(file)
|
87
|
+
config = MultiJson.load(contents, :symbolize_keys => true)
|
88
|
+
merged = deep_merge(@settings, config)
|
89
|
+
unless @loaded_files.empty?
|
90
|
+
changes = deep_diff(@settings, merged)
|
91
|
+
warning(changes, "config file applied changes")
|
92
|
+
end
|
93
|
+
@settings = merged
|
94
|
+
@indifferent_access = false
|
95
|
+
@loaded_files << file
|
96
|
+
rescue MultiJson::ParseError => error
|
97
|
+
warning(file, "config file must be valid json")
|
98
|
+
warning(file, "ignoring config file")
|
99
|
+
end
|
100
|
+
else
|
101
|
+
warning(file, "config file does not exist or is not readable")
|
102
|
+
warning(file, "ignoring config file")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Load settings from files in a directory. Files may be in
|
107
|
+
# nested directories.
|
108
|
+
#
|
109
|
+
# @param [String] directory path.
|
110
|
+
def load_directory(directory)
|
111
|
+
warning(directory, "loading config files from directory")
|
112
|
+
path = directory.gsub(/\\(?=\S)/, "/")
|
113
|
+
Dir.glob(File.join(path, "**/*.json")).each do |file|
|
114
|
+
load_file(file)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Set Sensu settings related environment variables. This method
|
119
|
+
# currently sets SENSU_CONFIG_FILES, a colon delimited list of
|
120
|
+
# loaded config files.
|
121
|
+
def set_env
|
122
|
+
ENV["SENSU_CONFIG_FILES"] = @loaded_files.join(":")
|
123
|
+
end
|
124
|
+
|
125
|
+
# Load settings from the environment and the paths provided, set
|
126
|
+
# appropriate environment variables.
|
127
|
+
#
|
128
|
+
# @param [Hash] options
|
129
|
+
# @option options [String] :config_file to load.
|
130
|
+
# @option options [String] :config_dir to load.
|
131
|
+
# @return [Hash] loaded settings.
|
132
|
+
def load(options={})
|
133
|
+
load_env
|
134
|
+
if options[:config_file]
|
135
|
+
load_file(options[:config_file])
|
136
|
+
end
|
137
|
+
if options[:config_dir]
|
138
|
+
load_directory(options[:config_dir])
|
139
|
+
end
|
140
|
+
set_env
|
141
|
+
to_hash
|
142
|
+
end
|
143
|
+
|
144
|
+
# Validate the loaded settings.
|
145
|
+
#
|
146
|
+
# @return [Array] validation failures.
|
147
|
+
def validate!
|
148
|
+
service = ::File.basename($0).split("-").last
|
149
|
+
validator = Validator.new
|
150
|
+
validator.run(@settings, service)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
# Retrieve setting category definitions.
|
156
|
+
#
|
157
|
+
# @param [Symbol] category to retrive.
|
158
|
+
# @return [Array<Hash>] category definitions.
|
159
|
+
def setting_category(category)
|
160
|
+
@settings[category].map do |name, details|
|
161
|
+
details.merge(:name => name.to_s)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Check to see if a definition exists in a category.
|
166
|
+
#
|
167
|
+
# @param [Symbol] category to inspect for the definition.
|
168
|
+
# @param [String] name of definition.
|
169
|
+
# @return [TrueClass, FalseClass]
|
170
|
+
def definition_exists?(category, name)
|
171
|
+
@settings[category].has_key?(name.to_sym)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Creates an indifferent hash.
|
175
|
+
#
|
176
|
+
# @return [Hash] indifferent hash.
|
177
|
+
def indifferent_hash
|
178
|
+
Hash.new do |hash, key|
|
179
|
+
if key.is_a?(String)
|
180
|
+
hash[key.to_sym]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Create a copy of a hash with indifferent access.
|
186
|
+
#
|
187
|
+
# @param hash [Hash] hash to make indifferent.
|
188
|
+
# @return [Hash] indifferent version of hash.
|
189
|
+
def with_indifferent_access(hash)
|
190
|
+
hash = indifferent_hash.merge(hash)
|
191
|
+
hash.each do |key, value|
|
192
|
+
if value.is_a?(Hash)
|
193
|
+
hash[key] = with_indifferent_access(value)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Update settings to have indifferent access.
|
199
|
+
def indifferent_access!
|
200
|
+
@settings = with_indifferent_access(@settings)
|
201
|
+
@indifferent_access = true
|
202
|
+
end
|
203
|
+
|
204
|
+
# Deep merge two hashes.
|
205
|
+
#
|
206
|
+
# @param [Hash] hash_one to serve as base.
|
207
|
+
# @param [Hash] hash_two to merge in.
|
208
|
+
def deep_merge(hash_one, hash_two)
|
209
|
+
merged = hash_one.dup
|
210
|
+
hash_two.each do |key, value|
|
211
|
+
merged[key] = case
|
212
|
+
when hash_one[key].is_a?(Hash) && value.is_a?(Hash)
|
213
|
+
deep_merge(hash_one[key], value)
|
214
|
+
when hash_one[key].is_a?(Array) && value.is_a?(Array)
|
215
|
+
hash_one[key].concat(value).uniq
|
216
|
+
else
|
217
|
+
value
|
218
|
+
end
|
219
|
+
end
|
220
|
+
merged
|
221
|
+
end
|
222
|
+
|
223
|
+
# Compare two hashes.
|
224
|
+
#
|
225
|
+
# @param [Hash] hash_one to compare.
|
226
|
+
# @param [Hash] hash_two to compare.
|
227
|
+
# @return [Hash] comparison diff hash.
|
228
|
+
def deep_diff(hash_one, hash_two)
|
229
|
+
keys = hash_one.keys.concat(hash_two.keys).uniq
|
230
|
+
keys.inject(Hash.new) do |diff, key|
|
231
|
+
unless hash_one[key] == hash_two[key]
|
232
|
+
if hash_one[key].is_a?(Hash) && hash_two[key].is_a?(Hash)
|
233
|
+
diff[key] = deep_diff(hash_one[key], hash_two[key])
|
234
|
+
else
|
235
|
+
diff[key] = [hash_one[key], hash_two[key]]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
diff
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Record a warning for an object.
|
243
|
+
#
|
244
|
+
# @param object [Object] under suspicion.
|
245
|
+
# @param message [String] warning message.
|
246
|
+
# @return [Array] current warnings.
|
247
|
+
def warning(object, message)
|
248
|
+
@warnings << {
|
249
|
+
:object => object,
|
250
|
+
:message => message
|
251
|
+
}
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Sensu
|
2
|
+
module Settings
|
3
|
+
module Rules
|
4
|
+
# Check that a value is a hash.
|
5
|
+
#
|
6
|
+
# @param value [Object] to check.
|
7
|
+
# @return [TrueClass, FalseClass]
|
8
|
+
def must_be_a_hash(value)
|
9
|
+
value.is_a?(Hash)
|
10
|
+
end
|
11
|
+
alias_method :is_a_hash?, :must_be_a_hash
|
12
|
+
|
13
|
+
# Check that a value is a hash, if set (not nil).
|
14
|
+
#
|
15
|
+
# @param value [Object] to check.
|
16
|
+
# @return [TrueClass, FalseClass]
|
17
|
+
def must_be_a_hash_if_set(value)
|
18
|
+
value.nil? ? true : must_be_a_hash(value)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Check that a value is an array.
|
22
|
+
#
|
23
|
+
# @param value [Object] to check.
|
24
|
+
# @return [TrueClass, FalseClass]
|
25
|
+
def must_be_an_array(value)
|
26
|
+
value.is_a?(Array)
|
27
|
+
end
|
28
|
+
alias_method :is_an_array?, :must_be_an_array
|
29
|
+
|
30
|
+
# Check that a value is an array, if set (not nil).
|
31
|
+
#
|
32
|
+
# @param value [Object] to check.
|
33
|
+
# @return [TrueClass, FalseClass]
|
34
|
+
def must_be_an_array_if_set(value)
|
35
|
+
value.nil? ? true : must_be_an_array(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Check that a value is a string.
|
39
|
+
#
|
40
|
+
# @param value [Object] to check.
|
41
|
+
# @return [TrueClass, FalseClass]
|
42
|
+
def must_be_a_string(value)
|
43
|
+
value.is_a?(String)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check that a value is a string, if set (not nil).
|
47
|
+
#
|
48
|
+
# @param value [Object] to check.
|
49
|
+
# @return [TrueClass, FalseClass]
|
50
|
+
def must_be_a_string_if_set(value)
|
51
|
+
value.nil? ? true : must_be_a_string(value)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check that a value is an integer.
|
55
|
+
#
|
56
|
+
# @param value [Object] to check.
|
57
|
+
# @return [TrueClass, FalseClass]
|
58
|
+
def must_be_an_integer(value)
|
59
|
+
value.is_a?(Integer)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check that a value is an integer, if set (not nil).
|
63
|
+
#
|
64
|
+
# @param value [Object] to check.
|
65
|
+
# @return [TrueClass, FalseClass]
|
66
|
+
def must_be_an_integer_if_set(value)
|
67
|
+
value.nil? ? true : must_be_an_integer(value)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Check that a value is numeric.
|
71
|
+
#
|
72
|
+
# @param value [Object] to check.
|
73
|
+
# @return [TrueClass, FalseClass]
|
74
|
+
def must_be_a_numeric(value)
|
75
|
+
value.is_a?(Numeric)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Check that a value is numeric, if set (not nil).
|
79
|
+
#
|
80
|
+
# @param value [Object] to check.
|
81
|
+
# @return [TrueClass, FalseClass]
|
82
|
+
def must_be_a_numeric_if_set(value)
|
83
|
+
value.nil? ? true : must_be_a_numeric(value)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Check that a value matches a regular expression.
|
87
|
+
#
|
88
|
+
# @param regex [Regexp] pattern to compare with value.
|
89
|
+
# @param value [Object] to check if matches pattern.
|
90
|
+
# @return [TrueClass, FalseClass]
|
91
|
+
def must_match_regex(regex, value)
|
92
|
+
value =~ regex
|
93
|
+
end
|
94
|
+
|
95
|
+
# Check if a value is boolean, if set (no nil).
|
96
|
+
#
|
97
|
+
# @param value [Object] to check.
|
98
|
+
# @return [TrueClass, FalseClass]
|
99
|
+
def must_be_boolean_if_set(value)
|
100
|
+
value.nil? ? true : (!!value == value)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Check that value items are all strings and not empty.
|
104
|
+
#
|
105
|
+
# @param value [Array] with items to check.
|
106
|
+
# @return [TrueClass, FalseClass]
|
107
|
+
def items_must_be_strings(value)
|
108
|
+
value.all? do |item|
|
109
|
+
item.is_a?(String) && !item.empty?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Check if either of the values are set (not nil).
|
114
|
+
#
|
115
|
+
# @param values [Array<Object>] to check if not nil.
|
116
|
+
# @return [TrueClass, FalseClass]
|
117
|
+
def either_are_set?(*values)
|
118
|
+
values.any? do |value|
|
119
|
+
!value.nil?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Check if values are valid times (can be parsed).
|
124
|
+
#
|
125
|
+
# @param values [Array<Object>] to check if valid time.
|
126
|
+
# @return [TrueClass, FalseClass]
|
127
|
+
def must_be_time(*values)
|
128
|
+
values.all? do |value|
|
129
|
+
Time.parse(value) rescue false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Check if values are allowed.
|
134
|
+
#
|
135
|
+
# @param allowed [Array<Object>] allowed values.
|
136
|
+
# @param values [Array<Object>] to check if allowed.
|
137
|
+
# @return [TrueClass, FalseClass]
|
138
|
+
def must_be_either(allowed, *values)
|
139
|
+
values.flatten.all? do |value|
|
140
|
+
allowed.include?(value)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Check if values are allowed, if set (not nil).
|
145
|
+
#
|
146
|
+
# @param allowed [Array<Object>] allowed values.
|
147
|
+
# @param values [Array<Object>] to check if allowed.
|
148
|
+
# @return [TrueClass, FalseClass]
|
149
|
+
def must_be_either_if_set(allowed, *values)
|
150
|
+
values[0].nil? ? true : must_be_either(allowed, values)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|