lib/json-schema/validator.rb in json-schema-2.2.5 vs lib/json-schema/validator.rb in json-schema-2.3.0

- old
+ new

@@ -5,105 +5,15 @@ require 'digest/sha1' require 'date' require 'thread' require 'yaml' +require 'json-schema/errors/schema_error' +require 'json-schema/errors/json_parse_error' + module JSON - class Schema - class ValidationError < StandardError - attr_accessor :fragments, :schema, :failed_attribute, :sub_errors - - def initialize(message, fragments, failed_attribute, schema) - @fragments = fragments.clone - @schema = schema - @sub_errors = [] - @failed_attribute = failed_attribute - message = "#{message} in schema #{schema.uri}" - super(message) - end - - def to_string - if @sub_errors.empty? - message - else - full_message = message + "\n The schema specific errors were:\n" - @sub_errors.each{|e| full_message = full_message + " - " + e.to_string + "\n"} - full_message - end - end - - def to_hash - base = {:schema => @schema.uri, :fragment => ::JSON::Schema::Attribute.build_fragment(fragments), :message => message, :failed_attribute => @failed_attribute.to_s.split(":").last.split("Attribute").first} - if !@sub_errors.empty? - base[:errors] = @sub_errors.map{|e| e.to_hash} - end - base - end - end - - class SchemaError < StandardError - end - - class JsonParseError < StandardError - end - - class Attribute - def self.validate(current_schema, data, fragments, processor, validator, options = {}) - end - - def self.build_fragment(fragments) - "#/#{fragments.join('/')}" - end - - def self.validation_error(processor, message, fragments, current_schema, failed_attribute, record_errors) - error = ValidationError.new(message, fragments, failed_attribute, current_schema) - if record_errors - processor.validation_error(error) - else - raise error - end - end - - def self.validation_errors(validator) - validator.validation_errors - end - end - - class Validator - attr_accessor :attributes, :uri - - def initialize() - @attributes = {} - @uri = nil - end - - def extend_schema_definition(schema_uri) - u = URI.parse(schema_uri) - validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"] - if validator.nil? - raise SchemaError.new("Schema not found: #{u.scheme}://#{u.host}#{u.path}") - end - @attributes.merge!(validator.attributes) - end - - def to_s - "#{@uri.scheme}://#{uri.host}#{uri.path}" - end - - def validate(current_schema, data, fragments, processor, options = {}) - current_schema.schema.each do |attr_name,attribute| - if @attributes.has_key?(attr_name.to_s) - @attributes[attr_name.to_s].validate(current_schema, data, fragments, processor, self, options) - end - end - data - end - end - end - - class Validator @@schemas = {} @@cache_schemas = false @@default_opts = { @@ -121,43 +31,16 @@ @@available_json_backends = [] @@json_backend = nil @@serializer = nil @@mutex = Mutex.new - def self.version_string_for(version) - # I'm not a fan of this, but it's quick and dirty to get it working for now - return "draft-04" unless version - case version.to_s - when "draft4", "http://json-schema.org/draft-04/schema#" - "draft-04" - when "draft3", "http://json-schema.org/draft-03/schema#" - "draft-03" - when "draft2" - "draft-02" - when "draft1" - "draft-01" - else - raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported") - end - end - - def self.metaschema_for(version_string) - File.join(Pathname.new(File.dirname(__FILE__)).parent.parent, "resources", "#{version_string}.json").to_s - end - def initialize(schema_data, data, opts={}) @options = @@default_opts.clone.merge(opts) @errors = [] - # I'm not a fan of this, but it's quick and dirty to get it working for now - version_string = "draft-04" - if @options[:version] - version_string = @options[:version] = self.class.version_string_for(@options[:version]) - u = URI.parse("http://json-schema.org/#{@options[:version]}/schema#") - validator = JSON::Validator.validators["#{u.scheme}://#{u.host}#{u.path}"] - @options[:version] = validator - end + validator = JSON::Validator.validator_for_name(@options[:version]) + @options[:version] = validator @validation_options = @options[:record_errors] ? {:record_errors => true} : {} @validation_options[:insert_defaults] = true if @options[:insert_defaults] @validation_options[:strict] = true if @options[:strict] == true @@ -167,14 +50,15 @@ # validate the schema, if requested if @options[:validate_schema] begin if @base_schema.schema["$schema"] - version_string = @options[:version] = self.class.version_string_for(@base_schema.schema["$schema"]) + base_validator = JSON::Validator.validator_for_name(@base_schema.schema["$schema"]) end + metaschema = base_validator ? base_validator.metaschema : validator.metaschema # Don't clear the cache during metaschema validation! - meta_validator = JSON::Validator.new(self.class.metaschema_for(version_string), @base_schema.schema, {:clear_cache => false}) + meta_validator = JSON::Validator.new(metaschema, @base_schema.schema, {:clear_cache => false}) meta_validator.validate rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError raise $! end end @@ -412,11 +296,11 @@ validator.validate end def fully_validate_schema(schema, opts={}) data = schema - schema = metaschema_for(version_string_for(opts[:version])) + schema = JSON::Validator.validator_for_name(opts[:version]).metaschema fully_validate(schema, data, opts) end def fully_validate_json(schema, data, opts={}) fully_validate(schema, data, opts.merge(:json => true)) @@ -449,18 +333,60 @@ def default_validator @@default_validator end + def validator_for_uri(schema_uri) + return default_validator unless schema_uri + u = URI.parse(schema_uri) + validator = validators["#{u.scheme}://#{u.host}#{u.path}"] + if validator.nil? + raise JSON::Schema::SchemaError.new("Schema not found: #{schema_uri}") + else + validator + end + end + + def validator_for_name(schema_name) + return default_validator unless schema_name + validator = validators_for_names([schema_name]).first + if validator.nil? + raise JSON::Schema::SchemaError.new("The requested JSON schema version is not supported") + else + validator + end + end + + alias_method :validator_for, :validator_for_uri + def register_validator(v) - @@validators[v.to_s] = v + @@validators["#{v.uri.scheme}://#{v.uri.host}#{v.uri.path}"] = v end def register_default_validator(v) @@default_validator = v end + def register_format_validator(format, validation_proc, versions = ["draft1", "draft2", "draft3", "draft4"]) + custom_format_validator = JSON::Schema::CustomFormat.new(validation_proc) + validators_for_names(versions).each do |validator| + validator.formats[format.to_s] = custom_format_validator + end + end + + def deregister_format_validator(format, versions = ["draft1", "draft2", "draft3", "draft4"]) + validators_for_names(versions).each do |validator| + validator.formats[format.to_s] = validator.default_formats[format.to_s] + end + end + + def restore_default_formats(versions = ["draft1", "draft2", "draft3", "draft4"]) + validators_for_names(versions).each do |validator| + validator.formats = validator.default_formats.clone + end + end + def json_backend if defined?(MultiJson) MultiJson.respond_to?(:adapter) ? MultiJson.adapter : MultiJson.engine else @@json_backend @@ -535,9 +461,18 @@ @@serializer = lambda{|o| Yajl::Encoder.encode(o) } else @@serializer = lambda{|o| YAML.dump(o) } + end + end + + private + + def validators_for_names(names) + names.map! { |name| name.to_s } + validators.reduce([]) do |memo, (_, validator)| + memo.tap { |m| m << validator if (validator.names & names).any? } end end end private