Module: RubstApi::Validator

Defined in:
lib/rubst_api/model.rb

Class Method Summary collapse

Class Method Details

.boolean(value) ⇒ Object

Raises:

  • (TypeError)


131
132
133
134
135
136
# File 'lib/rubst_api/model.rb', line 131

def boolean(value)
  return value if value == true || value == false
  return true if %w[1 true on yes].include?(value.to_s.downcase)
  return false if %w[0 false off no].include?(value.to_s.downcase)
  raise TypeError
end

.coerce(value, type, constraints = {}) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/rubst_api/model.rb', line 87

def coerce(value, type, constraints = {})
  return nil if value.nil? && nullable?(type, constraints)
  result =
    if type.is_a?(Array)
      validate_union(value, type, constraints)
    elsif type.is_a?(Hash) && type.key?(:array)
      Array(value).map { |item| coerce(item, type[:array], {}) }
    elsif type.is_a?(Class) && type <= Model
      type.validate(value, location: [])
    elsif type == String then String(value)
    elsif type == Integer then Integer(value, exception: true)
    elsif type == Float then Float(value)
    elsif type == Numeric then Float(value)
    elsif type == TrueClass || type == FalseClass || type == :boolean then boolean(value)
    elsif type == Symbol then value.to_sym
    elsif type == Date then Date.parse(value.to_s)
    elsif type == Time || type == DateTime then Time.parse(value.to_s)
    elsif type == URI then URI.parse(value.to_s)
    elsif type == Array then Array(value)
    elsif type == Hash then Hash(value)
    elsif type.respond_to?(:call) && !type.is_a?(Class) then type.call(value)
    elsif value.is_a?(type) then value
    else raise TypeError, "expected #{type}"
    end
  constrain(result, constraints)
rescue ValidationError
  raise
rescue StandardError
  raise ValidationError, [{ type: type_name(type), loc: [], msg: "Input should be a valid #{type_name(type)}", input: value }]
end

.constrain(value, options) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/rubst_api/model.rb', line 138

def constrain(value, options)
  length = value.respond_to?(:length) ? value.length : nil
  fail_constraint(value, "string_too_short", "Value is too short") if options[:min_length] && length && length < options[:min_length]
  fail_constraint(value, "string_too_long", "Value is too long") if options[:max_length] && length && length > options[:max_length]
  fail_constraint(value, "greater_than", "Value must be greater than #{options[:gt]}") if options[:gt] && !(value > options[:gt])
  fail_constraint(value, "greater_than_equal", "Value must be greater than or equal to #{options[:ge]}") if options[:ge] && !(value >= options[:ge])
  fail_constraint(value, "less_than", "Value must be less than #{options[:lt]}") if options[:lt] && !(value < options[:lt])
  fail_constraint(value, "less_than_equal", "Value must be less than or equal to #{options[:le]}") if options[:le] && !(value <= options[:le])
  pattern = options[:pattern] || options[:regex]
  fail_constraint(value, "string_pattern_mismatch", "Value does not match pattern") if pattern && !(Regexp.new(pattern.to_s) =~ value.to_s)
  fail_constraint(value, "enum", "Value is not an allowed option") if options[:enum] && !options[:enum].include?(value)
  value
end

.fail_constraint(value, type, message) ⇒ Object

Raises:



152
153
154
# File 'lib/rubst_api/model.rb', line 152

def fail_constraint(value, type, message)
  raise ValidationError, [{ type:, loc: [], msg: message, input: value }]
end

.nullable?(type, constraints) ⇒ Boolean

Returns:

  • (Boolean)


118
# File 'lib/rubst_api/model.rb', line 118

def nullable?(type, constraints) = constraints[:nullable] || (type.is_a?(Array) && type.include?(NilClass))

.type_name(type) ⇒ Object



156
157
158
159
# File 'lib/rubst_api/model.rb', line 156

def type_name(type)
  { Integer => "integer", Float => "number", String => "string", TrueClass => "boolean",
    FalseClass => "boolean", Hash => "object", Array => "array" }[type] || type.to_s.downcase
end

.validate_union(value, types, constraints) ⇒ Object

Raises:



120
121
122
123
124
125
126
127
128
129
# File 'lib/rubst_api/model.rb', line 120

def validate_union(value, types, constraints)
  return nil if value.nil? && types.include?(NilClass)
  failures = []
  types.reject { |type| type == NilClass }.each do |type|
    return coerce(value, type, constraints)
  rescue ValidationError => e
    failures.concat(e.errors)
  end
  raise ValidationError, failures
end