Module: Windoo::Validate

Defined in:
lib/windoo/validate.rb

Overview

A collection of methods implementing data constraints

Some of these methods can take multiple input types, such as a String or an number. All of them will either raise an exception if the value isn’t valid, or will return a standardized form of the input (e.g. a number or a Time, even if given a String)

Class Method Summary collapse

Class Method Details

.array_constraints(val, attr_def:, attr_name: nil) ⇒ Object

run the array constraint validations for an array value. The individual array items must already be validated



125
126
127
128
129
130
131
# File 'lib/windoo/validate.rb', line 125

def self.array_constraints(val, attr_def:, attr_name: nil)
  min_items val, min: attr_def[:minItems], attr_name: attr_name if attr_def[:minItems]
  max_items val, max: attr_def[:maxItems], attr_name: attr_name if attr_def[:maxItems]
  unique_array val, attr_name: attr_name if attr_def[:uniqueItems]

  val
end

.boolean(val, attr_name: nil, msg: nil) ⇒ Boolean

Confirm that the given value is a boolean value, accepting strings and symbols and returning real booleans as needed Accepts: true, false, ‘true’, ‘false’, ‘yes’, ‘no’, ‘t’,‘f’, ‘y’, or ‘n’ as strings or symbols, case insensitive

TODO: use this throughout ruby-jss

Parameters:

  • val (Boolean, String, Symbol)

    The value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Boolean)

    the valid boolean



170
171
172
173
174
175
176
# File 'lib/windoo/validate.rb', line 170

def self.boolean(val, attr_name: nil, msg: nil)
  return val if Windoo::TRUE_FALSE.include? val
  return true if val.to_s =~ /^(t(rue)?|y(es)?)$/i
  return false if val.to_s =~ /^(f(alse)?|no?)$/i

  raise_invalid_data_error(msg || "#{attr_name} value must be boolean true or false, or an equivalent string or symbol")
end

.class_instance(val, klass:, attr_name: nil, msg: nil) ⇒ Object

validate that a value is of a specific class

Parameters:

  • val (Object)

    The value to validate

  • klass (Class, Symbol)

    The class which the val must be an instance of

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Object)

    the valid value



143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/windoo/validate.rb', line 143

def self.class_instance(val, klass:, attr_name: nil, msg: nil)
  return val if val.instance_of? klass

  # try to instantiate the class with the value. It should raise an error
  # if not good
  klass.new val
rescue StandardError => e
  unless msg
    msg = +"#{attr_name} value must be a #{klass}, or #{klass}.new must accept it as the only parameter,"
    msg << "but #{klass}.new raised: #{e.class}: #{e}"
  end
  raise_invalid_data_error(msg)
end

.container_for_new_object(new_object_class:, container: nil) ⇒ Object?

confirm we were given a valid container when creating

Parameters:

  • new_object_class (Class)

    the class of the new object we are creating

  • container (Object) (defaults to: nil)

    The object that will contain tne new object we are creating

Returns:

  • (Object, nil)

    The valid container for the new object



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/windoo/validate.rb', line 529

def self.container_for_new_object(new_object_class:, container: nil)
  # software titles don't have containers.
  return if new_object_class == Windoo::SoftwareTitle

  unless container
    raise Windoo::UnsupportedError,
          'All new objects other than SoftwareTitle must pass in a container: object'
  end

  unless new_object_class::CONTAINER_CLASS == container.class
    raise Windoo::InvalidDataError,
          "The container for new #{new_object_class} objects must be a #{new_object_class::CONTAINER_CLASS} object"
  end

  container
end

.float(val, attr_name: nil, msg: nil) ⇒ Float

Confirm that a value is a Float or a string representation of a Float Return the Float, or raise an error

Parameters:

  • val (Object)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Float)


229
230
231
232
233
234
235
# File 'lib/windoo/validate.rb', line 229

def self.float(val, attr_name: nil, msg: nil)
  val = val.to_f if val.is_a?(Integer)
  val = val.to_f if val.is_a?(String) && (val.j_float? || val.j_integer?)
  return val if val.is_a? Float

  raise_invalid_data_error(msg || "#{attr_name} value must be an floating point number")
end

.fully_validate_integer(val, attr_def:, attr_name: nil) ⇒ Object

run all the possible validations on an integer



93
94
95
96
# File 'lib/windoo/validate.rb', line 93

def self.fully_validate_integer(val, attr_def:, attr_name: nil)
  val = integer val, attr_name: attr_name
  validate_numeric_constraints(val, attr_def: attr_def, attr_name: attr_name)
end

.fully_validate_number(val, attr_def:, attr_name: nil) ⇒ Object

run all the possible validations on a ‘number’



99
100
101
102
103
104
105
106
107
# File 'lib/windoo/validate.rb', line 99

def self.fully_validate_number(val, attr_def:, attr_name: nil)
  val =
    if %w[float double].include? attr_def[:format]
      float val, attr_name: attr_name
    else
      number val, attr_name: attr_name
    end
  validate_numeric_constraints(val, attr_def: attr_def, attr_name: attr_name)
end

.fully_validate_string(val, attr_def:, attr_name: nil) ⇒ Object

run all the possible validations on a string



82
83
84
85
86
87
88
89
90
# File 'lib/windoo/validate.rb', line 82

def self.fully_validate_string(val, attr_def:, attr_name: nil)
  val = string val, attr_name: attr_name

  min_length val, min: attr_def[:min_length], attr_name: attr_name if attr_def[:min_length]
  max_length val, max: attr_def[:max_length], attr_name: attr_name if attr_def[:max_length]
  matches_pattern val, attr_def[:pattern], attr_name: attr_name if attr_def[:pattern]

  val
end

.in_enum(val, enum:, attr_name: nil, msg: nil) ⇒ Object

Does a value exist in a given enum array?

Parameters:

  • val (Object)

    The thing that must be in the enum

  • enum (Array)

    the enum of allowed values

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Object)

    The valid object



498
499
500
501
502
# File 'lib/windoo/validate.rb', line 498

def self.in_enum(val, enum:, attr_name: nil, msg: nil)
  return val if enum.include? val

  raise_invalid_data_error(msg || "#{attr_name} value must be one of: #{enum.join ', '}")
end

.integer(val, attr_name: nil, msg: nil) ⇒ Integer

Confirm that a value is an integer or a string representation of an integer. Return the integer, or raise an error

Parameters:

  • val (Object)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Integer)


213
214
215
216
217
218
# File 'lib/windoo/validate.rb', line 213

def self.integer(val, attr_name: nil, msg: nil)
  val = val.to_i if val.is_a?(String) && val.j_integer?
  return val if val.is_a? Integer

  raise_invalid_data_error(msg || "#{attr_name} value must be an integer")
end

.json_attr(val, attr_def:, attr_name: nil) ⇒ Boolean

Validate that a value is valid based on its definition in an objects OAPI_PROPERTIES constant.

Parameters:

  • val (Object)

    The value to validate

  • klass (Class, Symbol)

    The class which the val must be

  • msg (String)

    A custom error message when the value is invalid

Returns:

  • (Boolean)

    the valid boolean



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/windoo/validate.rb', line 36

def self.json_attr(val, attr_def:, attr_name: nil)
  # check that the new val is not nil if its required
  val = not_nil(val, attr_name: attr_name) if attr_def[:required]

  # if the new val is nil here, then nil is OK andd we shouldn't
  # check anything else
  return val if val.nil?

  val =
    case attr_def[:class]
    when :Boolean
      boolean val, attr_name: attr_name

    when :String
      fully_validate_string(val, attr_def: attr_def, attr_name: attr_name)

    when :Integer
      fully_validate_integer(val, attr_def: attr_def, attr_name: attr_name)

    when :Number
      fully_validate_number(val, attr_def: attr_def, attr_name: attr_name)

    when :Hash
      hash val, attr_name: attr_name

    when :Symbol
      symbol val, attr_name: attr_name

    when :Time
      time val, attr_name: attr_name

    else
      val
    end # case

  # Now that the val is in whatever correct format after the above tests,
  # we test for enum membership if needed
  # otherwise, just return the val
  if attr_def[:enum]
    in_enum val, enum: attr_def[:enum], attr_name: attr_name
  else
    val
  end
end

.matches_pattern(val, pattern:, attr_name: nil, msg: nil) ⇒ Object

Does a string match a given regular expression?

Parameters:

  • val (String)

    The value to match

  • pattern (pattern)

    the regular expression

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Object)

    The valid object



514
515
516
517
518
# File 'lib/windoo/validate.rb', line 514

def self.matches_pattern(val, pattern:, attr_name: nil, msg: nil)
  return val if val =~ pattern

  raise_invalid_data_error(msg || "#{attr_name} value does not match RegExp: #{pattern}")
end

.max_items(val, max:, attr_name: nil, msg: nil) ⇒ String

validate that the given value contains no more than some maximum number of items

While this is intended for Arrays, it will work for any object that responds to #size

Parameters:

  • val (Object)

    the value to validate

  • max (Object)

    the maximum number of items allowed

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value

Raises:

  • (ArgumentError)


452
453
454
455
456
457
# File 'lib/windoo/validate.rb', line 452

def self.max_items(val, max:, attr_name: nil, msg: nil)
  raise ArgumentError, 'max must be a number' unless max.is_a?(Numeric)
  return val if val.size <= max

  raise_invalid_data_error(msg || "#{attr_name} value must contain no more than #{max} items")
end

.max_length(val, max:, attr_name: nil, msg: nil) ⇒ String

validate that the given value’s length is less than or equal to some maximum

While this is intended for Strings, it will work for any object that responds to #length

Parameters:

  • val (Object)

    the value to validate

  • max (Object)

    the maximum length allowed

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value

Raises:

  • (ArgumentError)


412
413
414
415
416
417
# File 'lib/windoo/validate.rb', line 412

def self.max_length(val, max:, attr_name: nil, msg: nil)
  raise ArgumentError, 'max must be a number' unless max.is_a?(Numeric)
  return val if val.length <= max

  raise_invalid_data_error(msg || "length of #{attr_name} value must be <= #{max}")
end

.maximum(val, max:, attr_name: nil, exclusive: false, msg: nil) ⇒ String

validate that the given value is less than or equal to some maximum

While intended for Numbers, this will work for any Comparable objects

If exclusive, the max value is excluded from the range and the value must be less than the max.

Parameters:

  • val (Object)

    the thing to validate

  • max (Object)

    A value that the val must be less than or equal to

  • exclusuve (Boolean)

    Should the max be excluded from the valid range? true: val must be < max, false: val must be <= max

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value



347
348
349
350
351
352
353
354
# File 'lib/windoo/validate.rb', line 347

def self.maximum(val, max:, attr_name: nil, exclusive: false, msg: nil)
  if exclusive
    return val if val < max
  elsif val <= max
    return val
  end
  raise_invalid_data_error(msg || "#{attr_name} value must be <= #{max}")
end

.min_items(val, min:, attr_name: nil, msg: nil) ⇒ String

validate that the given value contains at least some minimum number of items

While this is intended for Arrays, it will work for any object that responds to #size

Parameters:

  • val (Object)

    the value to validate

  • min (Object)

    the minimum number of items allowed

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value

Raises:

  • (ArgumentError)


432
433
434
435
436
437
# File 'lib/windoo/validate.rb', line 432

def self.min_items(val, min:, attr_name: nil, msg: nil)
  raise ArgumentError, 'min must be a number' unless min.is_a?(Numeric)
  return val if val.size >= min

  raise_invalid_data_error(msg || "#{attr_name} value must contain at least #{min} items")
end

.min_length(val, min:, attr_name: nil, msg: nil) ⇒ String

validate that the given value’s length is greater than or equal to some minimum

While this is intended for Strings, it will work for any object that responds to #length

Parameters:

  • val (Object)

    the value to validate

  • min (Object)

    The minimum length allowed

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value

Raises:

  • (ArgumentError)


392
393
394
395
396
397
# File 'lib/windoo/validate.rb', line 392

def self.min_length(val, min:, attr_name: nil, msg: nil)
  raise ArgumentError, 'min must be a number' unless min.is_a?(Numeric)
  return val if val.length >= min

  raise_invalid_data_error(msg || "length of #{attr_name} value must be >= #{min}")
end

.minimum(val, min:, attr_name: nil, exclusive: false, msg: nil) ⇒ String

validate that the given value is greater than or equal to some minimum

If exclusive, the min value is excluded from the range and the value must be greater than the min.

While intended for Numbers, this will work for any Comparable objects

Parameters:

  • val (Object)

    the thing to validate

  • min (Object)

    A value that the val must be greater than or equal to

  • exclusuve (Boolean)

    Should the min be excluded from the valid range? true: val must be > min, false: val must be >= min

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value



320
321
322
323
324
325
326
327
# File 'lib/windoo/validate.rb', line 320

def self.minimum(val, min:, attr_name: nil, exclusive: false, msg: nil)
  if exclusive
    return val if val > min
  elsif val >= min
    return val
  end
  raise_invalid_data_error(msg || "#{attr_name} value must be >= #{min}")
end

.multiple_of(val, multiplier:, attr_name: nil, msg: nil) ⇒ String

Validate that a given number is multiple of some other given number

Parameters:

  • val (Number)

    the number to validate

  • multiplier (Number)

    the number what the val must be a multiple of. this must be positive.

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (String)

    the valid value

Raises:



367
368
369
370
371
372
373
374
375
376
377
# File 'lib/windoo/validate.rb', line 367

def self.multiple_of(val, multiplier:, attr_name: nil, msg: nil)
  unless multiplier.is_a?(Numeric) && multiplier.positive?
    raise ArgumentError,
          'multiplier must be a positive number'
  end
  raise Windoo::InvalidDataError, 'Value must be a number' unless val.is_a?(Numeric)

  return val if (val % multiplier).zero?

  raise_invalid_data_error(msg || "#{attr_name} value must be a multiple of #{multiplier}")
end

.not_nil(val, attr_name: nil, msg: nil) ⇒ Object

validate that a value is not nil

Parameters:

  • val (Object)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Object)

    the valid value



482
483
484
485
486
# File 'lib/windoo/validate.rb', line 482

def self.not_nil(val, attr_name: nil, msg: nil)
  return val unless val.nil?

  raise_invalid_data_error(msg || "#{attr_name} value may not be nil")
end

.number(val, attr_name: nil, msg: nil) ⇒ Integer

Confirm that a value is an number or a string representation of an number. Return the number, or raise an error

Parameters:

  • val (Object)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Integer)


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/windoo/validate.rb', line 187

def self.number(val, attr_name: nil, msg: nil)
  if val.ia_a?(Integer) || val.is_a?(Float)
    return val

  elsif val.is_a?(String)

    if val.j_integer?
      return val.to_i
    elsif val.j_float?
      return val.to_f
    end

  end

  raise_invalid_data_error(msg || "#{attr_name} value must be a number")
end

.object(val, attr_name: nil, msg: nil) ⇒ Hash

Confirm that a value is a Hash Return the Hash, or raise an error

Parameters:

  • val (Object)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Hash)


246
247
248
249
250
# File 'lib/windoo/validate.rb', line 246

def self.object(val, attr_name: nil, msg: nil)
  return val if val.is_a? Hash

  raise_invalid_data_error(msg || "#{attr_name} value must be a Hash")
end

.raise_invalid_data_error(msg) ⇒ Object

Thes methods all raise this error



21
22
23
# File 'lib/windoo/validate.rb', line 21

def self.raise_invalid_data_error(msg)
  raise Windoo::InvalidDataError, msg
end

.string(val, attr_name: nil, msg: nil, to_s: false) ⇒ Hash

Confirm that a value is a String Return the String, or raise an error

Parameters:

  • val (Object)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

  • to_s: (Boolean) (defaults to: false)

    If true, this method always succeds and returns the result of calling #to_s on the value

Returns:

  • (Hash)


295
296
297
298
299
300
# File 'lib/windoo/validate.rb', line 295

def self.string(val, attr_name: nil, msg: nil, to_s: false)
  val = val.to_s if to_s
  return val if val.is_a? String

  raise_invalid_data_error(msg || "#{attr_name} value must be a String")
end

.symbol(val, attr_name: nil, msg: nil) ⇒ Symbol

Confirm that a value is a Symbol, or can be coerced into one Return the Symbol, or raise an error

Parameters:

  • val (#to_sym)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Symbol)


261
262
263
264
265
# File 'lib/windoo/validate.rb', line 261

def self.symbol(val, attr_name: nil, msg: nil)
  return val.to_sym if val.respond_to? :to_sym

  raise_invalid_data_error(msg || "#{attr_name} value must be a Symbol")
end

.time(val, attr_name: nil, msg: nil) ⇒ Time

Confirm that a value is a Time, or can be parsed into

Return the ISO8601 formatted String, or raise an error

Parameters:

  • val (Time, #to_s)

    the value to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

Returns:

  • (Time)

    The Time



277
278
279
280
281
# File 'lib/windoo/validate.rb', line 277

def self.time(val, attr_name: nil, msg: nil)
  val.is_a?(Time) ? val : Time.parse(val.to_s)
rescue StandardError
  raise_invalid_data_error(msg || "#{attr_name} value must be a Time, or a String to be used with Time.parse")
end

.unique_array(val, attr_name: nil, msg: nil) ⇒ Object

validate that an array has only unique items, no duplicate values

Parameters:

  • val (Array)

    The array to validate

  • msg (String) (defaults to: nil)

    A custom error message when the value is invalid

  • return (Array)

    the valid array

Raises:

  • (ArgumentError)


467
468
469
470
471
472
# File 'lib/windoo/validate.rb', line 467

def self.unique_array(val, attr_name: nil, msg: nil)
  raise ArgumentError, 'Value must be an Array' unless val.is_a?(Array)
  return val if val.uniq.size == val.size

  raise_invalid_data_error(msg || "#{attr_name} value must contain only unique items")
end

.validate_numeric_constraints(val, attr_def:, attr_name: nil) ⇒ Object

run the numeric constraint validations for any numeric value The number itself must already be validated



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/windoo/validate.rb', line 111

def self.validate_numeric_constraints(val, attr_def:, attr_name: nil)
  ex_min = attr_def[:exclusive_minimum]
  ex_max = attr_def[:exclusive_maximum]
  mult_of = attr_def[:multiple_of]

  minimum val, min: attr_def[:minimum], exclusive: ex_min, attr_name: attr_name if attr_def[:minimum]
  maximum val, max: attr_def[:maximum], exclusive: ex_max, attr_name: attr_name if attr_def[:maximum]
  multiple_of val, multiplier: mult_of, attr_name: attr_name if mult_of

  val
end