Class: AutoParse::Instance
- Inherits:
-
Object
- Object
- AutoParse::Instance
- Defined in:
- lib/autoparse/instance.rb
Class Method Summary collapse
- .additional_properties_schema ⇒ Object
- .data ⇒ Object
- .dereference ⇒ Object
- .description ⇒ Object
- .keys ⇒ Object
- .properties ⇒ Object
- .property(property_name) ⇒ Object
- .property_dependencies ⇒ Object
- .uri ⇒ Object
- .validate_array_property(property_value, schema_data) ⇒ Object
- .validate_boolean_property(property_value, schema_data) ⇒ Object
- .validate_integer_property(property_value, schema_data) ⇒ Object
- .validate_number_property(property_value, schema_data) ⇒ Object
- .validate_object_property(property_value, schema_data) ⇒ Object
- .validate_property_value(property_value, schema_data) ⇒ Object private
- .validate_string_property(property_value, schema_data) ⇒ Object
- .validate_union_property(property_value, schema_data) ⇒ Object
Instance Method Summary collapse
- #[](key, raw = false) ⇒ Object
- #[]=(key, raw = false, value = :undefined) ⇒ Object
-
#initialize(data = {}) ⇒ Instance
constructor
A new instance of Instance.
-
#inspect ⇒ String
Returns a
String
representation of the schema instance. - #method_missing(method, *params, &block) ⇒ Object
- #to_hash ⇒ Object
-
#to_json(*args) ⇒ String
Converts the instance value to JSON.
-
#valid? ⇒ Boolean
Validates the parsed data against the schema.
Constructor Details
#initialize(data = {}) ⇒ Instance
Returns a new instance of Instance.
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/autoparse/instance.rb', line 284 def initialize(data={}) if (self.class.data || {})['type'] == nil # Type is omitted, default value is any. else type_set = [(self.class.data || {})['type']].flatten.compact if !type_set.include?('object') raise TypeError, "Only schemas of type 'object' are instantiable:\n" + "#{self.class.data.inspect}" end end if data.respond_to?(:to_hash) data = data.to_hash elsif data.respond_to?(:to_json) data = JSON.parse(data.to_json) else raise TypeError, 'Unable to parse. ' + 'Expected data to respond to either :to_hash or :to_json.' end if data['$ref'] raise TypeError, "Cannot instantiate a reference schema. Must be dereferenced first." end @data = data end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *params, &block) ⇒ Object
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/autoparse/instance.rb', line 311 def method_missing(method, *params, &block) schema_data = self.class.data unless schema_data['additionalProperties'] # Do nothing special if additionalProperties is not set. super else # We can't modify the method in-place because this affects the call # to super. property_name = method.to_s assignment = false # Property names simply identify the property and thus don't # include the assignment operator. if property_name[-1..-1] == '=' assignment = true property_name[-1..-1] = '' end property_key = self.class.keys[property_name] property_schema = self.class.properties[property_key] # TODO: Properly support additionalProperties. if property_key == nil || property_schema == nil # Method not found. return super end # If additionalProperties is simply set to true, no parsing takes # place and all values are treated as 'any'. if assignment new_value = params[0] __set__(property_name, new_value) else __get__(property_name) end end end |
Class Method Details
.additional_properties_schema ⇒ Object
98 99 100 |
# File 'lib/autoparse/instance.rb', line 98 def self.additional_properties_schema return EMPTY_SCHEMA end |
.data ⇒ Object
106 107 108 |
# File 'lib/autoparse/instance.rb', line 106 def self.data return @schema_data ||= {} end |
.dereference ⇒ Object
30 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 |
# File 'lib/autoparse/instance.rb', line 30 def self.dereference if @schema_data['$ref'] # Dereference the schema if necessary. ref_uri = Addressable::URI.parse(@schema_data['$ref']) if self.uri schema_uri = self.uri + Addressable::URI.parse(@schema_data['$ref']) else if ref_uri.relative? warn("Schema URI is relative, could not resolve against parent.") else warn("Could not resolve URI against parent.") end schema_uri = ref_uri end schema_class = AutoParse.schemas[schema_uri] warn("Schema URI mismatch.") if schema_class.uri != schema_uri if schema_class == nil raise ArgumentError, "Could not find schema: #{@schema_data['$ref']}. " + "Referenced schema must be parsed first." else return schema_class end else return self end end |
.description ⇒ Object
110 111 112 |
# File 'lib/autoparse/instance.rb', line 110 def self.description return self.data['description'] end |
.keys ⇒ Object
88 89 90 91 92 93 94 95 96 |
# File 'lib/autoparse/instance.rb', line 88 def self.keys return @keys ||= ( if self.superclass.ancestors.include?(::AutoParse::Instance) self.superclass.keys.dup else {} end ) end |
.properties ⇒ Object
59 60 61 62 63 64 65 66 67 |
# File 'lib/autoparse/instance.rb', line 59 def self.properties return @properties ||= ( if self.superclass.ancestors.include?(::AutoParse::Instance) self.superclass.properties.dup else {} end ) end |
.property(property_name) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/autoparse/instance.rb', line 69 def self.property(property_name) property_key = self.keys[property_name] || property_name schema_class = self.properties[property_key] if !schema_class if self.data[property_name] schema_class = AutoParse.generate(self.data[property_name], :parent => self) else schema_class = self.additional_properties_schema end end if schema_class.data['$ref'] # Dereference the schema if necessary. schema_class = schema_class.dereference # Avoid this dereference in the future. self.properties[property_key] = schema_class end return schema_class end |
.property_dependencies ⇒ Object
102 103 104 |
# File 'lib/autoparse/instance.rb', line 102 def self.property_dependencies return @property_dependencies ||= {} end |
.uri ⇒ Object
24 25 26 27 28 |
# File 'lib/autoparse/instance.rb', line 24 def self.uri return (@uri ||= (@schema_data ? Addressable::URI.parse(@schema_data['id']) : nil) ) end |
.validate_array_property(property_value, schema_data) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/autoparse/instance.rb', line 151 def self.validate_array_property(property_value, schema_data) if property_value.respond_to?(:to_ary) property_value = property_value.to_ary else return false end property_value.each do |item_value| unless self.validate_property_value(item_value, schema_data['items']) return false end end return true end |
.validate_boolean_property(property_value, schema_data) ⇒ Object
124 125 126 127 128 |
# File 'lib/autoparse/instance.rb', line 124 def self.validate_boolean_property(property_value, schema_data) return false if property_value != true && property_value != false # TODO: implement more than type-checking return true end |
.validate_integer_property(property_value, schema_data) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/autoparse/instance.rb', line 130 def self.validate_integer_property(property_value, schema_data) return false if !property_value.kind_of?(Integer) if schema_data['minimum'] && schema_data['exclusiveMinimum'] return false if property_value <= schema_data['minimum'] elsif schema_data['minimum'] return false if property_value < schema_data['minimum'] end if schema_data['maximum'] && schema_data['exclusiveMaximum'] return false if property_value >= schema_data['maximum'] elsif schema_data['maximum'] return false if property_value > schema_data['maximum'] end return true end |
.validate_number_property(property_value, schema_data) ⇒ Object
145 146 147 148 149 |
# File 'lib/autoparse/instance.rb', line 145 def self.validate_number_property(property_value, schema_data) return false if !property_value.kind_of?(Numeric) # TODO: implement more than type-checking return true end |
.validate_object_property(property_value, schema_data) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/autoparse/instance.rb', line 165 def self.validate_object_property(property_value, schema_data) if property_value.kind_of?(Instance) return property_value.valid? else # This is highly ineffecient, but currently hard to avoid given the # schema is anonymous, making lookups very difficult. schema = AutoParse.generate(schema_data, :parent => self) begin return schema.new(property_value).valid? rescue TypeError, ArgumentError, ::JSON::ParserError return false end end end |
.validate_property_value(property_value, schema_data) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/autoparse/instance.rb', line 222 def self.validate_property_value(property_value, schema_data) if property_value == nil && schema_data['required'] == true return false elsif property_value == nil # Value was omitted, but not required. Still valid. return true end # Verify property values if schema_data['$ref'] if self.uri schema_uri = self.uri + Addressable::URI.parse(schema_data['$ref']) else schema_uri = Addressable::URI.parse(schema_data['$ref']) end schema = AutoParse.schemas[schema_uri] if schema == nil raise ArgumentError, "Could not find schema: #{schema_data['$ref']}. " + "Referenced schema must be parsed first." end schema_data = schema.data end case schema_data['type'] when 'string' return false unless self.validate_string_property( property_value, schema_data ) when 'boolean' return false unless self.validate_boolean_property( property_value, schema_data ) when 'integer' return false unless self.validate_integer_property( property_value, schema_data ) when 'number' return false unless self.validate_number_property( property_value, schema_data ) when 'array' return false unless self.validate_array_property( property_value, schema_data ) when 'object' return false unless self.validate_object_property( property_value, schema_data ) when 'null' return false unless property_value.nil? when Array return false unless self.validate_union_property( property_value, schema_data ) else # Either type 'any' or we don't know what this is, # default to anything goes. Validation of an 'any' property always # succeeds. end return true end |
.validate_string_property(property_value, schema_data) ⇒ Object
114 115 116 117 118 119 120 121 122 |
# File 'lib/autoparse/instance.rb', line 114 def self.validate_string_property(property_value, schema_data) property_value = property_value.to_str rescue property_value if !property_value.kind_of?(String) return false else # TODO: implement more than type-checking return true end end |
.validate_union_property(property_value, schema_data) ⇒ 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 210 211 212 213 214 215 216 217 218 |
# File 'lib/autoparse/instance.rb', line 180 def self.validate_union_property(property_value, schema_data) union = schema_data['type'] possible_types = [union].flatten.compact for type in possible_types case type when 'string' return true if self.validate_string_property( property_value, schema_data ) when 'boolean' return true if self.validate_boolean_property( property_value, schema_data ) when 'integer' return true if self.validate_integer_property( property_value, schema_data ) when 'number' return true if self.validate_number_property( property_value, schema_data ) when 'array' return true if self.validate_array_property( property_value, schema_data ) when 'object' return true if self.validate_object_property( property_value, schema_data ) when 'null' return true if property_value.nil? when 'any' return true end end # None of the union types validated. # An empty union will fail to validate anything. return false end |
Instance Method Details
#[](key, raw = false) ⇒ Object
371 372 373 374 375 376 377 |
# File 'lib/autoparse/instance.rb', line 371 def [](key, raw=false) if raw == true return @data[key] else return self.__get__(key) end end |
#[]=(key, raw = false, value = :undefined) ⇒ Object
379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/autoparse/instance.rb', line 379 def []=(key, raw=false, value=:undefined) if value == :undefined # Due to the way Ruby handles default values in assignment methods, # we have to swap some values around here. raw, value = false, raw end if raw == true return @data[key] = value else return self.__set__(key, value) end end |
#inspect ⇒ String
Returns a String
representation of the schema instance.
469 470 471 472 473 474 |
# File 'lib/autoparse/instance.rb', line 469 def inspect sprintf( "#<%s:%#0x DATA:%s>", self.class.to_s, self.object_id, self.to_hash.inspect ) end |
#to_hash ⇒ Object
449 450 451 |
# File 'lib/autoparse/instance.rb', line 449 def to_hash return @data end |
#to_json(*args) ⇒ String
Note:
Ignores extra arguments to avoid throwing errors w/ certain JSON libraries.
Converts the instance value to JSON.
461 462 463 |
# File 'lib/autoparse/instance.rb', line 461 def to_json(*args) return MultiJson.dump(self.to_hash) end |
#valid? ⇒ Boolean
Validates the parsed data against the schema.
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/autoparse/instance.rb', line 394 def valid? unvalidated_fields = @data.keys.dup for property_key, schema_class in self.class.properties property_value = @data[property_key] if !self.class.validate_property_value( property_value, schema_class.data) return false end if property_value == nil && schema_class.data['required'] != true # Value was omitted, but not required. Still valid. Skip dependency # checks. next end # Verify property dependencies property_dependencies = self.class.property_dependencies[property_key] case property_dependencies when String, Array property_dependencies = [property_dependencies].flatten for dependency_key in property_dependencies dependency_value = @data[dependency_key] return false if dependency_value == nil end when Class if property_dependencies.ancestors.include?(Instance) dependency_instance = property_dependencies.new(property_value) return false unless dependency_instance.valid? else raise TypeError, "Expected schema Class, got #{property_dependencies.class}." end end end if self.class.additional_properties_schema == nil # No additional properties allowed return false unless unvalidated_fields.empty? elsif self.class.additional_properties_schema != EMPTY_SCHEMA # Validate all remaining fields against this schema for property_key in unvalidated_fields property_value = @data[property_key] if !self.class.additional_properties_schema.validate_property_value( property_value, self.class.additional_properties_schema.data) return false end end end if self.class.superclass && self.class.superclass != Instance && self.class.ancestors.first != Instance # The spec actually only defined the 'extends' semantics as children # must also validate aainst the parent. return false unless self.class.superclass.new(@data).valid? end return true end |