Skip to content

Commit

Permalink
Cache schema
Browse files Browse the repository at this point in the history
It reduces a lot of duplicated object creation when you want to apply
the same schema but different data

```
schema = ....
validator = JSON::Validator.new(schema)

validator.validate(data1)
validator.validate(data2)
validator.validate(data3)
```
  • Loading branch information
ganmacs committed Feb 16, 2022
1 parent 57e90a7 commit d6e45e5
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 10 deletions.
22 changes: 12 additions & 10 deletions lib/json-schema/validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Validator
@@serializer = nil
@@mutex = Mutex.new

def initialize(schema_data, data, opts={})
def initialize(schema_data, opts={})
@options = @@default_opts.clone.merge(opts)
@errors = []

Expand All @@ -50,15 +50,13 @@ def initialize(schema_data, data, opts={})
@validation_options[:clear_cache] = true if !@@cache_schemas || @options[:clear_cache]

@@mutex.synchronize { @base_schema = initialize_schema(schema_data, configured_validator) }
@original_data = data
@data = initialize_data(data)
@@mutex.synchronize { build_schemas(@base_schema) }

# validate the schema, if requested
if @options[:validate_schema]
# Don't clear the cache during metaschema validation!
meta_validator = self.class.new(@base_schema.validator.metaschema, @base_schema.schema, {:clear_cache => false})
meta_validator.validate
meta_validator = self.class.new(@base_schema.validator.metaschema, {:clear_cache => false})
meta_validator.validate(@base_schema.schema)
end

# If the :fragment option is set, try and validate against the fragment
Expand Down Expand Up @@ -102,8 +100,10 @@ def schema_from_fragment(base_schema, fragment)
end

# Run a simple true/false validation of data against a schema
def validate
@base_schema.validate(@data,[],self,@validation_options)
def validate(data)
original_data = data
data = initialize_data(data)
@base_schema.validate(data,[],self,@validation_options)

if @options[:record_errors]
if @options[:errors_as_objects]
Expand All @@ -115,11 +115,13 @@ def validate
true
end
ensure
@errors = []

if @validation_options[:clear_cache] == true
self.class.clear_cache
end
if @validation_options[:insert_defaults]
self.class.merge_missing_values(@data, @original_data)
self.class.merge_missing_values(data, original_data)
end
end

Expand Down Expand Up @@ -243,8 +245,8 @@ def validate_uri(schema, data, opts={})
end

def validate!(schema, data,opts={})
validator = new(schema, data, opts)
validator.validate
validator = new(schema, opts)
validator.validate(data)
end

def validate2(schema, data, opts={})
Expand Down
35 changes: 35 additions & 0 deletions test/full_validation_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,41 @@ def test_full_validation
assert(errors.length == 2)
end

def test_full_validation_with_instantiated_validator
data = {"b" => {"a" => 5}}
schema = {
"type" => "object",
"required" => ["b"],
"properties" => {
"b" => {
}
}
}

validator = JSON::Validator.new(schema, { record_errors: true })
assert(validator.validate(data).empty?)
assert(validator.validate(data).empty?)
assert(validator.validate(data).empty?)

data = {"c" => 5}
schema = {
"type" => "object",
"required" => ["b"],
"properties" => {
"b" => {
},
"c" => {
"type" => "string"
}
}
}

validator = JSON::Validator.new(schema, { record_errors: true })
assert(validator.validate(data).length == 2)
assert(validator.validate(data).length == 2)
assert(validator.validate(data).length == 2)
end

def test_full_validation_with_union_types
data = {"b" => 5}
schema = {
Expand Down
223 changes: 223 additions & 0 deletions test/initialize_data_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,227 @@ def test_parse_hash

assert_raises(TypeError) { JSON::Validator.validate(schema, data, :uri => true) }
end

def test_parse_character_string_with_instantiated_validator
schema = {'type' => 'string'}
data = 'hello world'

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
end

def test_parse_integer_string_with_instantiated_validator
schema = {'type' => 'integer'}
data = '42'

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert_raises(JSON::Schema::ValidationError) { v.validate(data) }
assert_raises(JSON::Schema::ValidationError) { v.validate(data) }

v = JSON::Validator.new(schema, { :json => true })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
end

def test_parse_hash_string_with_instantiated_validator
schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } }
data = '{"a": "b"}'

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert_raises(JSON::Schema::ValidationError) { v.validate(data) }
assert_raises(JSON::Schema::ValidationError) { v.validate(data) }

v = JSON::Validator.new(schema, { :json => true })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::UriError) { v.validate(data) }
assert_raises(JSON::Schema::UriError) { v.validate(data) }
end

def test_parse_json_string_with_instantiated_validator
schema = {'type' => 'string'}
data = '"hello world"'

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
end

def test_parse_plain_text_string_with_instantiated_validator
schema = {'type' => 'string'}
data = 'kapow'

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
end

def test_parse_valid_uri_string_with_instantiated_validator
schema = {'type' => 'string'}
data = 'http://foo.bar/'

stub_request(:get, "foo.bar").to_return(:body => '"hello world"', :status => 200)

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert(v.validate(data))
assert(v.validate(data))
end

def test_parse_invalid_uri_string_with_instantiated_validator
schema = {'type' => 'string'}
data = 'http://foo.bar/'

stub_request(:get, "foo.bar").to_timeout

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

stub_request(:get, "foo.bar").to_return(:status => [500, "Internal Server Error"])

assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
assert_raises(JSON::Schema::JsonLoadError) { v.validate(data) }
end

def test_parse_invalid_scheme_string_with_instantiated_validator
schema = {'type' => 'string'}
data = 'pick one: [1, 2, 3]'

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }
assert_raises(JSON::Schema::JsonParseError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(JSON::Schema::UriError) { v.validate(data) }
assert_raises(JSON::Schema::UriError) { v.validate(data) }
end

def test_parse_integer_with_instantiated_validator
schema = {'type' => 'integer'}
data = 42

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(TypeError) { v.validate(data) }
assert_raises(TypeError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(TypeError) { v.validate(data) }
assert_raises(TypeError) { v.validate(data) }
end

def test_parse_hash_with_instantiated_validator
schema = { 'type' => 'object', 'properties' => { 'a' => { 'type' => 'string' } } }
data = { 'a' => 'b' }

v = JSON::Validator.new(schema)
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :parse_data => false })
assert(v.validate(data))
assert(v.validate(data))

v = JSON::Validator.new(schema, { :json => true })
assert_raises(TypeError) { v.validate(data) }
assert_raises(TypeError) { v.validate(data) }

v = JSON::Validator.new(schema, { :uri => true })
assert_raises(TypeError) { v.validate(data) }
assert_raises(TypeError) { v.validate(data) }
end
end

0 comments on commit d6e45e5

Please sign in to comment.