Class: JTD::Schema
- Inherits:
-
Object
- Object
- JTD::Schema
- Defined in:
- lib/jtd/schema.rb
Overview
Represents a JSON Type Definition schema.
Class Method Summary collapse
-
.from_hash(hash) ⇒ Object
Constructs a Schema from a Hash like the kind produced by JSON#parse.
Instance Method Summary collapse
-
#form ⇒ Object
Returns the form that the schema takes on.
-
#verify(root = self) ⇒ Object
Raises a TypeError or ArgumentError if the Schema is not correct according to the JSON Type Definition specification.
Class Method Details
.from_hash(hash) ⇒ Object
Constructs a Schema from a Hash like the kind produced by JSON#parse.
In other words, #from_hash is meant to be used to convert some parsed JSON into a Schema.
If hash isn’t a Hash or contains keys that are illegal for JSON Type Definition, then #from_hash will raise a TypeError.
If the properties of hash are not of the correct type for a JSON Type Definition schema (for example, if the “elements” property of hash is non-nil, but not a hash), then #from_hash may raise a NoMethodError.
31 32 33 34 35 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 80 81 82 83 84 85 86 87 |
# File 'lib/jtd/schema.rb', line 31 def self.from_hash(hash) # Raising this error early makes for a much clearer error for the # relatively common case of something that was expected to be an object # (Hash), but was something else instead. raise TypeError.new("expected hash, got: #{hash}") unless hash.is_a?(Hash) illegal_keywords = hash.keys - KEYWORDS unless illegal_keywords.empty? raise TypeError.new("illegal schema keywords: #{illegal_keywords}") end s = Schema.new if hash['metadata'] s. = hash['metadata'] end unless hash['nullable'].nil? s.nullable = hash['nullable'] end if hash['definitions'] s.definitions = Hash[hash['definitions'].map { |k, v| [k, from_hash(v) ]}] end s.ref = hash['ref'] s.type = hash['type'] s.enum = hash['enum'] if hash['elements'] s.elements = from_hash(hash['elements']) end if hash['properties'] s.properties = Hash[hash['properties'].map { |k, v| [k, from_hash(v) ]}] end if hash['optionalProperties'] s.optional_properties = Hash[hash['optionalProperties'].map { |k, v| [k, from_hash(v) ]}] end unless hash['additionalProperties'].nil? s.additional_properties = hash['additionalProperties'] end if hash['values'] s.values = from_hash(hash['values']) end s.discriminator = hash['discriminator'] if hash['mapping'] s.mapping = Hash[hash['mapping'].map { |k, v| [k, from_hash(v) ]}] end s end |
Instance Method Details
#form ⇒ Object
Returns the form that the schema takes on.
The return value will be one of :empty, :ref:, :type, :enum, :elements, :properties, :values, or :discriminator.
If the schema is not well-formed, i.e. calling #verify on it raises an error, then the return value of #form is not well-defined.
209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/jtd/schema.rb', line 209 def form return :ref if ref return :type if type return :enum if enum return :elements if elements return :properties if properties || optional_properties return :values if values return :discriminator if discriminator :empty end |
#verify(root = self) ⇒ Object
Raises a TypeError or ArgumentError if the Schema is not correct according to the JSON Type Definition specification.
See the JSON Type Definition specification for more details, but a high level #verify checks such things as:
-
Making sure each of the attributes of the Schema are of the right type,
-
The Schema uses a valid combination of JSON Type Definition keywords,
-
The Schema isn’t ambiguous or unsatisfiable.
-
The Schema doesn’t make references to nonexistent definitions.
If root is specified, then that root is assumed to contain the schema being verified. By default, the Schema is considered its own root, which is usually the desired behavior.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/jtd/schema.rb', line 103 def verify(root = self) self.check_type('metadata', [Hash]) self.check_type('nullable', [TrueClass, FalseClass]) self.check_type('definitions', [Hash]) self.check_type('ref', [String]) self.check_type('type', [String]) self.check_type('enum', [Array]) self.check_type('elements', [Schema]) self.check_type('properties', [Hash]) self.check_type('optional_properties', [Hash]) self.check_type('additional_properties', [TrueClass, FalseClass]) self.check_type('values', [Schema]) self.check_type('discriminator', [String]) self.check_type('mapping', [Hash]) form_signature = [ !!ref, !!type, !!enum, !!elements, !!properties, !!optional_properties, !!additional_properties, !!values, !!discriminator, !!mapping, ] unless VALID_FORMS.include?(form_signature) raise ArgumentError.new("invalid schema form: #{self}") end if root != self && definitions && definitions.any? raise ArgumentError.new("non-root definitions: #{definitions}") end if ref if !root.definitions || !root.definitions.key?(ref) raise ArgumentError.new("ref to non-existent definition: #{ref}") end end if type && !TYPES.include?(type) raise ArgumentError.new("invalid type: #{type}") end if enum if enum.empty? raise ArgumentError.new("enum must not be empty: #{self}") end if enum.any? { |v| !v.is_a?(String) } raise ArgumentError.new("enum must contain only strings: #{enum}") end if enum.size != enum.uniq.size raise ArgumentError.new("enum must not contain duplicates: #{enum}") end end if properties && optional_properties shared_keys = properties.keys & optional_properties.keys if shared_keys.any? raise ArgumentError.new("properties and optional_properties share keys: #{shared_keys}") end end if mapping mapping.values.each do |s| if s.form != :properties raise ArgumentError.new("mapping values must be of properties form: #{s}") end if s.nullable raise ArgumentError.new("mapping values must not be nullable: #{s}") end contains_discriminator = ArgumentError.new("mapping values must not contain discriminator (#{discriminator}): #{s}") if s.properties && s.properties.key?(discriminator) raise contains_discriminator end if s.optional_properties && s.optional_properties.key?(discriminator) raise contains_discriminator end end end definitions.values.each { |s| s.verify(root) } if definitions elements.verify(root) if elements properties.values.each { |s| s.verify(root) } if properties optional_properties.values.each { |s| s.verify(root) } if optional_properties values.verify(root) if values mapping.values.each { |s| s.verify(root) } if mapping self end |