Class: Frise::Validator
- Inherits:
-
Object
- Object
- Frise::Validator
- Defined in:
- lib/frise/validator.rb
Overview
Checks if a pre-loaded config object conforms to a schema file.
The validate and validate_at static methods read schema files and validates config objects against the parsed schema. They can optionally be initialized with a set of user-defined validators that can be used in the schema files for custom validations.
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
Class Method Summary collapse
- .parse_symbols(obj) ⇒ Object
- .validate(config, schema_file, options = {}) ⇒ Object
- .validate_at(config, at_path, schema_file, options = {}) ⇒ Object
- .validate_obj(config, schema, options = {}) ⇒ Object
- .validate_obj_at(config, at_path, schema, path_prefix: nil, validators: nil, print: nil, fatal: nil, raise_error: nil) ⇒ Object
Instance Method Summary collapse
- #add_validation_error(path, msg) ⇒ Object
- #get_expected_types(full_schema) ⇒ Object
- #get_full_schema(schema) ⇒ Object
-
#initialize(root, validators = nil) ⇒ Validator
constructor
A new instance of Validator.
- #validate_constant(full_schema, obj, path) ⇒ Object
- #validate_custom(full_schema, obj, path) ⇒ Object
- #validate_enum(full_schema, obj, path) ⇒ Object
- #validate_object(path, obj, schema) ⇒ Object
- #validate_one_of(full_schema, obj, path) ⇒ Object
- #validate_optional(full_schema, obj, path) ⇒ Object
- #validate_remaining_keys(full_schema, obj, path, processed_keys) ⇒ Object
- #validate_spec_keys(full_schema, obj, path, processed_keys) ⇒ Object
- #validate_type(full_schema, obj, path) ⇒ Object
- #widened_class(obj) ⇒ Object
Constructor Details
Instance Attribute Details
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
14 15 16 |
# File 'lib/frise/validator.rb', line 14 def errors @errors end |
Class Method Details
.parse_symbols(obj) ⇒ Object
167 168 169 170 171 172 173 174 |
# File 'lib/frise/validator.rb', line 167 def self.parse_symbols(obj) case obj when Array then obj.map { |e| parse_symbols(e) } when Hash then obj.to_h { |k, v| [parse_symbols(k), parse_symbols(v)] } when String then obj.start_with?('$') ? obj[1..].to_sym : obj else obj end end |
.validate(config, schema_file, options = {}) ⇒ Object
211 212 213 |
# File 'lib/frise/validator.rb', line 211 def self.validate(config, schema_file, = {}) validate_obj_at(config, [], Parser.parse(schema_file) || { allow_unknown_keys: true }, **) end |
.validate_at(config, at_path, schema_file, options = {}) ⇒ Object
215 216 217 |
# File 'lib/frise/validator.rb', line 215 def self.validate_at(config, at_path, schema_file, = {}) validate_obj_at(config, at_path, Parser.parse(schema_file) || { allow_unknown_keys: true }, **) end |
.validate_obj(config, schema, options = {}) ⇒ Object
176 177 178 |
# File 'lib/frise/validator.rb', line 176 def self.validate_obj(config, schema, = {}) validate_obj_at(config, [], schema, **) end |
.validate_obj_at(config, at_path, schema, path_prefix: nil, validators: nil, print: nil, fatal: nil, raise_error: nil) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/frise/validator.rb', line 180 def self.validate_obj_at( config, at_path, schema, path_prefix: nil, validators: nil, print: nil, fatal: nil, raise_error: nil ) schema = parse_symbols(schema) at_path.reverse.each { |key| schema = { key => schema, :allow_unknown_keys => true } } validator = Validator.new(config, validators) validator.validate_object((path_prefix || []).join('.'), config, schema) if validator.errors.any? if print puts "#{validator.errors.length} config error(s) found:" validator.errors.each do |error| puts " - #{error}" end end exit 1 if fatal raise ValidationError.new(validator.errors), 'Invalid configuration' if raise_error end validator.errors end |
Instance Method Details
#add_validation_error(path, msg) ⇒ Object
31 32 33 34 |
# File 'lib/frise/validator.rb', line 31 def add_validation_error(path, msg) logged_path = path.empty? ? '<root>' : path @errors << "At #{logged_path}: #{msg}" end |
#get_expected_types(full_schema) ⇒ Object
66 67 68 69 70 71 72 |
# File 'lib/frise/validator.rb', line 66 def get_expected_types(full_schema) type_key = full_schema.fetch(:type, 'Hash') allowed_types = %w[Hash Array String Integer Float Object] return [Object.const_get(type_key)] if allowed_types.include?(type_key) return [TrueClass, FalseClass] if type_key == 'Boolean' raise "Invalid expected type in schema: #{type_key}" end |
#get_full_schema(schema) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/frise/validator.rb', line 36 def get_full_schema(schema) case schema when Hash default_type = schema[:enum] || schema[:one_of] || schema.key?(:constant) ? 'Object' : 'Hash' { type: default_type }.merge(schema) when Symbol then { type: 'Object', validate: schema } when Array if schema.size == 1 { type: 'Array', all: schema[0] } else (raise "Invalid schema: #{schema.inspect}") end when String if schema.end_with?('?') { type: schema[0..-2], optional: true } else { type: schema } end else raise "Invalid schema: #{schema.inspect}" end end |
#validate_constant(full_schema, obj, path) ⇒ Object
117 118 119 120 121 122 123 |
# File 'lib/frise/validator.rb', line 117 def validate_constant(full_schema, obj, path) if full_schema.key?(:constant) && full_schema[:constant] != obj add_validation_error(path, "invalid value #{obj.inspect}. " \ "The only accepted value is #{full_schema[:constant]}") end true end |
#validate_custom(full_schema, obj, path) ⇒ Object
84 85 86 87 88 89 90 91 92 93 |
# File 'lib/frise/validator.rb', line 84 def validate_custom(full_schema, obj, path) if full_schema[:validate] begin @validators.method(full_schema[:validate]).call(@root, obj) rescue StandardError => e add_validation_error(path, e.) end end true end |
#validate_enum(full_schema, obj, path) ⇒ Object
95 96 97 98 99 100 101 102 |
# File 'lib/frise/validator.rb', line 95 def validate_enum(full_schema, obj, path) if full_schema[:enum] && !full_schema[:enum].include?(obj) add_validation_error(path, "invalid value #{obj.inspect}. " \ "Accepted values are #{full_schema[:enum].map(&:inspect).join(', ')}") return false end true end |
#validate_object(path, obj, schema) ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/frise/validator.rb', line 152 def validate_object(path, obj, schema) full_schema = get_full_schema(schema) return unless validate_optional(full_schema, obj, path) return unless validate_type(full_schema, obj, path) return unless validate_custom(full_schema, obj, path) return unless validate_enum(full_schema, obj, path) return unless validate_one_of(full_schema, obj, path) return unless validate_constant(full_schema, obj, path) processed_keys = Set.new return unless validate_spec_keys(full_schema, obj, path, processed_keys) validate_remaining_keys(full_schema, obj, path, processed_keys) end |
#validate_one_of(full_schema, obj, path) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/frise/validator.rb', line 104 def validate_one_of(full_schema, obj, path) if full_schema[:one_of] full_schema[:one_of].each do |schema_opt| opt_validator = Validator.new(@root, @validators) opt_validator.validate_object(path, obj, schema_opt) return true if opt_validator.errors.empty? end add_validation_error(path, "#{obj.inspect} does not match any of the possible schemas") return false end true end |
#validate_optional(full_schema, obj, path) ⇒ Object
58 59 60 61 62 63 64 |
# File 'lib/frise/validator.rb', line 58 def validate_optional(full_schema, obj, path) if obj.nil? add_validation_error(path, 'missing required value') unless full_schema[:optional] return false end true end |
#validate_remaining_keys(full_schema, obj, path, processed_keys) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/frise/validator.rb', line 134 def validate_remaining_keys(full_schema, obj, path, processed_keys) expected_types = get_expected_types(full_schema) if expected_types.size == 1 && expected_types[0].ancestors.member?(Enumerable) hash = obj.is_a?(Hash) ? obj : obj.map.with_index { |x, i| [i, x] }.to_h hash.each do |key, value| validate_object(path, key, full_schema[:all_keys]) if full_schema[:all_keys] && !key.is_a?(Symbol) next if processed_keys.member? key if full_schema[:all] validate_object(path.empty? ? key : "#{path}.#{key}", value, full_schema[:all]) elsif !full_schema[:allow_unknown_keys] add_validation_error(path, "unknown key: #{key}") end end end true end |
#validate_spec_keys(full_schema, obj, path, processed_keys) ⇒ Object
125 126 127 128 129 130 131 132 |
# File 'lib/frise/validator.rb', line 125 def validate_spec_keys(full_schema, obj, path, processed_keys) full_schema.each do |spec_key, spec_value| next if spec_key.is_a?(Symbol) validate_object(path.empty? ? spec_key : "#{path}.#{spec_key}", obj[spec_key], spec_value) processed_keys << spec_key end true end |
#validate_type(full_schema, obj, path) ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/frise/validator.rb', line 74 def validate_type(full_schema, obj, path) expected_types = get_expected_types(full_schema) unless expected_types.any? { |typ| obj.is_a?(typ) } type_key = full_schema.fetch(:type, 'Hash') add_validation_error(path, "expected #{type_key}, found #{widened_class(obj)}") return false end true end |
#widened_class(obj) ⇒ Object
24 25 26 27 28 29 |
# File 'lib/frise/validator.rb', line 24 def widened_class(obj) class_name = obj.class.to_s return 'Boolean' if %w[TrueClass FalseClass].include? class_name return 'Integer' if %w[Fixnum Bignum].include? class_name class_name end |