diff --git a/json-schema.gemspec b/json-schema.gemspec index a1370f60..1ee467ab 100644 --- a/json-schema.gemspec +++ b/json-schema.gemspec @@ -19,7 +19,6 @@ Gem::Specification.new do |s| s.license = "MIT" s.required_rubygems_version = ">= 1.8" - s.add_runtime_dependency "addressable" s.add_development_dependency "webmock" s.add_runtime_dependency "addressable", '~> 2.3' end diff --git a/lib/json-schema/schema.rb b/lib/json-schema/schema.rb index e3c2d70b..18f998c8 100644 --- a/lib/json-schema/schema.rb +++ b/lib/json-schema/schema.rb @@ -55,6 +55,13 @@ def base_uri parts.join('/') + '/' end + # @return [JSON::Schema] a new schema matching an array whose items all match this schema. + def to_array_schema + array_schema = { 'type' => 'array', 'items' => schema } + array_schema['$schema'] = schema['$schema'] unless schema['$schema'].nil? + JSON::Schema.new(array_schema, uri, validator) + end + def to_s @schema.to_json end diff --git a/lib/json-schema/validator.rb b/lib/json-schema/validator.rb index dd27ac9a..8c3ae52f 100644 --- a/lib/json-schema/validator.rb +++ b/lib/json-schema/validator.rb @@ -7,6 +7,7 @@ require 'thread' require 'yaml' +require 'json-schema/schema/loader' require 'json-schema/errors/schema_error' require 'json-schema/errors/json_parse_error' @@ -39,6 +40,7 @@ def initialize(schema_data, data, opts={}) validator = JSON::Validator.validator_for_name(@options[:version]) @options[:version] = validator + @options[:schema_loader] ||= JSON::Validator.schema_loader @validation_options = @options[:record_errors] ? {:record_errors => true} : {} @validation_options[:insert_defaults] = true if @options[:insert_defaults] @@ -100,9 +102,10 @@ def schema_from_fragment(base_schema, fragment) end end if @options[:list] #check if the schema is validating a list - base_schema.schema = schema_to_list(base_schema.schema) + base_schema.to_array_schema + else + base_schema end - base_schema end # Run a simple true/false validation of data against a schema @@ -145,7 +148,7 @@ def load_ref_schema(parent_schema,ref) end if Validator.schemas[uri.to_s].nil? - schema = JSON::Schema.new(JSON::Validator.parse(open(uri.to_s.chomp('#')).read), uri, @options[:version]) + schema = @options[:schema_loader].load(uri) Validator.add_schema(schema) build_schemas(schema) end @@ -238,7 +241,7 @@ def build_schemas(parent_schema) def handle_schema(parent_schema, obj) if obj.is_a?(Hash) schema_uri = parent_schema.uri.clone - schema = JSON::Schema.new(obj,schema_uri,parent_schema.validator) + schema = JSON::Schema.new(obj, schema_uri, parent_schema.validator) if obj['id'] Validator.add_schema(schema) end @@ -309,6 +312,14 @@ def fully_validate_uri(schema, data, opts={}) fully_validate(schema, data, opts.merge(:uri => true)) end + def schema_loader + @@schema_loader ||= JSON::Schema::Loader.new + end + + def schema_loader=(loader) + @@schema_loader = loader + end + def clear_cache @@schemas = {} if @@cache_schemas == false end @@ -503,53 +514,43 @@ def fake_uuid schema @@fake_uuid_generator.call(schema) end - def schema_to_list(schema) - new_schema = {"type" => "array", "items" => schema} - if !schema["$schema"].nil? - new_schema["$schema"] = schema["$schema"] - end - - new_schema - end - def initialize_schema(schema) if schema.is_a?(String) begin # Build a fake URI for this schema_uri = Addressable::URI.parse(fake_uuid(schema)) - schema = JSON::Validator.parse(schema) + schema = JSON::Schema.new(JSON::Validator.parse(schema), schema_uri, @options[:version]) if @options[:list] && @options[:fragment].nil? - schema = schema_to_list(schema) + schema = schema.to_array_schema end - schema = JSON::Schema.new(schema,schema_uri,@options[:version]) Validator.add_schema(schema) rescue # Build a uri for it schema_uri = normalized_uri(schema) if Validator.schemas[schema_uri.to_s].nil? - schema = JSON::Validator.parse(open(schema_uri.to_s).read) + schema = @options[:schema_loader].load(schema_uri) + if @options[:list] && @options[:fragment].nil? - schema = schema_to_list(schema) + schema = schema.to_array_schema end - schema = JSON::Schema.new(schema,schema_uri,@options[:version]) + Validator.add_schema(schema) else schema = Validator.schemas[schema_uri.to_s] if @options[:list] && @options[:fragment].nil? - schema = schema_to_list(schema.schema) - schema_uri = Addressable::URI.parse(fake_uuid(serialize(schema))) - schema = JSON::Schema.new(schema, schema_uri, @options[:version]) + schema = schema.to_array_schema + schema.uri = Addressable::URI.parse(fake_uuid(serialize(schema.schema))) Validator.add_schema(schema) end schema end end elsif schema.is_a?(Hash) + schema_uri = Addressable::URI.parse(fake_uuid(serialize(schema))) + schema = JSON::Schema.new(schema, schema_uri, @options[:version]) if @options[:list] && @options[:fragment].nil? - schema = schema_to_list(schema) + schema = schema.to_array_schema end - schema_uri = Addressable::URI.parse(fake_uuid(serialize(schema))) - schema = JSON::Schema.new(schema,schema_uri,@options[:version]) Validator.add_schema(schema) else raise "Invalid schema - must be either a string or a hash" @@ -558,7 +559,6 @@ def initialize_schema(schema) schema end - def initialize_data(data) if @options[:json] data = JSON::Validator.parse(data) diff --git a/test/test_validator.rb b/test/test_validator.rb new file mode 100644 index 00000000..a460e716 --- /dev/null +++ b/test/test_validator.rb @@ -0,0 +1,50 @@ +require File.expand_path('../test_helper', __FILE__) + +class TestValidator < Minitest::Test + + class MockLoader + def load(location) + schema = { + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'type' => 'string', + 'minLength' => 2 + } + + JSON::Schema.new(schema, Addressable::URI.parse(location.to_s)) + end + end + + def teardown + # Hacky. Not sure of a better tactic for resetting just yet. + JSON::Validator.send(:class_variable_set, :@@schema_loader, nil) + end + + def test_default_schema_loader + loader = JSON::Validator.schema_loader + assert loader.accept_uri?(Addressable::URI.parse('http://example.com')) + assert loader.accept_file?(Pathname.new('/etc/passwd')) + end + + def test_set_default_schema_loader + JSON::Validator.schema_loader = MockLoader.new + + schema = { '$ref' => 'http://any.url/at/all' } + assert_valid schema, 'abc' + refute_valid schema, 'a' + end + + def test_validate_with_loader + loader = MockLoader.new + schema = { '$ref' => 'http://any.url/at/all' } + assert_valid schema, 'abc', :schema_loader => loader + refute_valid schema, 'a', :schema_loader => loader + end + + def test_validate_list_with_loader + loader = MockLoader.new + schema = { '$ref' => 'http://what.ever/schema' } + assert_valid schema, ['abc', 'def'], :schema_loader => loader, :list => true + refute_valid schema, ['abc', 'a'], :schema_loader => loader, :list => true + end + +end