jschema 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4564a821e55e9336df4d64292765c8e366e1e262
4
- data.tar.gz: 990b4033d9123af1ee0b471d6117f2f570a250e1
3
+ metadata.gz: 27963957079b8a36781b058fbfcc5a65d05bc982
4
+ data.tar.gz: a095f424265697a18c1ba7b27359a4352393d06c
5
5
  SHA512:
6
- metadata.gz: cccc98abfaf52295b9d7f959a94bfbc2ea01310f71893018e586a2137dfaf72d93cca14ffe75af29326bd1aeed3ce511c8b452c2dc0d0999bd85d02a6bc9f233
7
- data.tar.gz: 5f4c8f525be757b44e21626a846ca45484fb13eb9690a199cfb40da244726e2b00a7e83e331cee81385267baa6002d16b400f786847e277370bc177247208f26
6
+ metadata.gz: 9868e28a5802ba5102e443adfdcdd1342371157a5aa77a7a6a0adfe84a6ed56024436c5ee9a232524f4c36d15a16a2a25fb53f51fb68067dfbaae85144c7138a
7
+ data.tar.gz: 2455390ded90bb4a99f6f64cdc48a7bc29d2d4b55a3763b0e052fdb3b71555c092a3aeca3e068404763128893e39263b329182c2427c3856d779b0fc52571c03
@@ -4,6 +4,7 @@ require 'delegate'
4
4
 
5
5
  require 'jschema/json_reference'
6
6
  require 'jschema/schema_ref'
7
+ require 'jschema/schema_uri'
7
8
  require 'jschema/schema'
8
9
  require 'jschema/simple_validator'
9
10
  require 'jschema/validator'
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module JSchema
2
4
  module JSONReference
3
5
  @mutex = Mutex.new
@@ -5,30 +7,66 @@ module JSchema
5
7
 
6
8
  class << self
7
9
  def register_schema(schema)
8
- schema_key = key(schema.uri, schema)
10
+ schema_key = key(normalize(schema.uri), schema)
11
+
9
12
  @mutex.synchronize do
10
13
  @schemas[schema_key] = schema
11
14
  end
12
15
  end
13
16
 
14
17
  def dereference(uri, schema)
15
- schema_key = key(uri, schema)
16
- @mutex.synchronize do
17
- @schemas[schema_key]
18
- end || build_external_schema(uri, schema)
18
+ schema_key = key(expand_uri(uri, schema), schema)
19
+ cached_schema = @mutex.synchronize do
20
+ @schemas[schema_key] if schema_key
21
+ end
22
+
23
+ if cached_schema
24
+ cached_schema
25
+ elsif uri.absolute? && !schema_part?(uri, schema)
26
+ build_external_schema(uri, schema)
27
+ end
19
28
  end
20
29
 
21
30
  private
22
31
 
32
+ def expand_uri(uri, schema)
33
+ if schema && schema.uri.absolute?
34
+ normalize schema.uri.merge(uri)
35
+ else
36
+ normalize uri
37
+ end
38
+ end
39
+
40
+ def normalize(uri)
41
+ normalized = uri.dup
42
+ normalized.fragment = nil if normalized.fragment == ''
43
+ normalized.normalize
44
+ end
45
+
46
+ def schema_part?(uri, schema)
47
+ if schema
48
+ uri1_base = uri.dup
49
+ uri1_base.fragment = ''
50
+
51
+ uri2_base = schema.uri.dup
52
+ uri2_base.fragment = ''
53
+
54
+ uri1_base == uri2_base
55
+ else
56
+ false
57
+ end
58
+ end
59
+
23
60
  def build_external_schema(uri, schema)
24
61
  unless valid_external_uri?(uri)
25
- fail InvalidSchema, 'Invalid URI for external schema'
62
+ fail InvalidSchema, "Invalid URI for external schema: #{uri}"
26
63
  end
27
64
 
28
- sch = JSON.parse download_schema(uri)
29
- register_schema Schema.new(sch, uri, schema.parent)
65
+ schema_data = JSON.parse download_schema(uri)
66
+ parent_schema = schema && schema.parent
67
+ Schema.build(schema_data, parent_schema, uri.to_s)
30
68
  rescue JSON::ParserError, Timeout::Error
31
- raise InvalidSchema, 'Failed to download external schema'
69
+ raise InvalidSchema, "Failed to download external schema: #{uri}"
32
70
  end
33
71
 
34
72
  def valid_external_uri?(uri)
@@ -40,16 +78,20 @@ module JSchema
40
78
  request['Accept'] = 'application/json+schema'
41
79
 
42
80
  http = Net::HTTP.new(uri.hostname, uri.port)
43
- http.read_timeout = 1
81
+ http.read_timeout = 3
82
+ http.open_timeout = 2
44
83
  http.continue_timeout = 1
45
- http.open_timeout = 1
46
84
 
47
85
  http.request(request).body
48
86
  end
49
87
 
50
88
  def key(uri, schema)
51
- root_schema = root(schema)
52
- "#{root_schema.object_id}:#{uri}"
89
+ if schema
90
+ root_schema = root(schema)
91
+ "#{root_schema.object_id}:#{uri}"
92
+ else
93
+ uri.to_s
94
+ end
53
95
  end
54
96
 
55
97
  def root(schema)
@@ -0,0 +1,70 @@
1
+ require 'logger'
2
+
3
+ # Unobtrusive logger for method calls.
4
+ module JSchema
5
+ module MethodCallLogger
6
+ class << self
7
+ def enable(log_class)
8
+ sub_classes = [log_class]
9
+ sub_classes.each do |klass|
10
+ # We want to observe only the given class and all its subclasses
11
+ observe_class klass
12
+
13
+ klass.constants.each do |class_sym|
14
+ sub_class = klass.const_get(class_sym)
15
+
16
+ # FIXME: misleading condintion
17
+ if sub_class.is_a?(Module) &&
18
+ sub_class.name.start_with?(log_class.name)
19
+
20
+ sub_classes << sub_class
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def log_method_call(klass, method, args)
27
+ logger.info "#{klass.name}##{method}(#{args.join(', ')})"
28
+ end
29
+
30
+ private
31
+
32
+ def logger
33
+ @logger ||= Logger.new(STDOUT)
34
+ end
35
+
36
+ def observe_class(observable)
37
+ # Override public instance methods
38
+ observable.public_instance_methods(false).each do |method|
39
+ observable.class_eval do
40
+
41
+ # Redefine each public method so that each method call is logged
42
+ # first and then it is forwarded to the origin method
43
+
44
+ original_method = public_instance_method(method)
45
+
46
+ define_method(method) do |*args, &block|
47
+ MethodCallLogger.log_method_call self.class, method, args
48
+ original_method.bind(self).call(*args, &block)
49
+ end
50
+ end
51
+ end
52
+
53
+ # Override public "class" methods
54
+ observable.methods(false).each do |method|
55
+ original_method = observable.method(method).unbind
56
+ observable.define_singleton_method(method) do |*args, &block|
57
+
58
+ # FIXME: The following line causes ruby (2.1.2 p95) to crash
59
+ # MethodCallLogger.log_method_call observable, method, args
60
+ # But it works when the method is inserted "inline"
61
+ logger = Logger.new(STDOUT)
62
+ logger.info "#{observable.name}.#{method}(#{args.join(', ')})"
63
+
64
+ original_method.bind(self).call(*args, &block)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -9,13 +9,15 @@ module JSchema
9
9
  check_schema_version schema
10
10
 
11
11
  if (json_reference = schema['$ref'])
12
- ref_uri = URI(json_reference)
13
- SchemaRef.new(ref_uri, parent)
12
+ unescaped_ref = json_reference.gsub(/~1|~0/, '~1' => '/', '~0' => '~')
13
+ SchemaRef.new(URI(unescaped_ref), parent)
14
14
  else
15
- uri = establish_uri(schema, parent, id)
16
- jschema = new(schema, uri, parent)
17
- register_definitions schema, jschema
18
- JSONReference.register_schema jschema
15
+ uri = SchemaURI.build(schema['id'], parent, id)
16
+ parent && JSONReference.dereference(uri, parent) || begin
17
+ jschema = new(schema, uri, parent)
18
+ register_definitions schema, jschema
19
+ JSONReference.register_schema jschema
20
+ end
19
21
  end
20
22
  end
21
23
 
@@ -28,25 +30,6 @@ module JSchema
28
30
  end
29
31
  end
30
32
 
31
- # rubocop:disable MethodLength
32
- def establish_uri(schema, parent, id)
33
- this_id = URI(schema['id'] || id || '#')
34
-
35
- # RFC 3986, cl. 5.1
36
- if parent
37
- if parent.uri.absolute?
38
- parent.uri.merge(this_id).normalize
39
- elsif parent.uri.path.empty?
40
- URI('#' + File.join(parent.uri.fragment, id || '')) # FIXME
41
- else
42
- # RFC 3986, cl. 5.1.4
43
- fail InvalidSchema, 'Can not establish a base URI'
44
- end
45
- else
46
- this_id
47
- end
48
- end
49
-
50
33
  def register_definitions(schema, parent)
51
34
  if (definitions = schema['definitions'])
52
35
  definitions.each do |definition, sch|
@@ -7,7 +7,10 @@ module JSchema
7
7
  end
8
8
 
9
9
  def __getobj__
10
- @schema ||= JSONReference.dereference(@uri, @parent)
10
+ @schema ||= begin
11
+ JSONReference.dereference(@uri, @parent) ||
12
+ Kernel.fail(InvalidSchema, "Failed to dereference schema: #{@uri}")
13
+ end
11
14
  end
12
15
  end
13
16
  end
@@ -0,0 +1,37 @@
1
+ module JSchema
2
+ class SchemaURI
3
+ class << self
4
+ # String, Schema, String
5
+ def build(schema_id, parent_schema, id)
6
+ # RFC 3986, cl. 5.1
7
+ if parent_schema
8
+ if parent_schema.uri.absolute?
9
+ new_uri_part = schema_id ||
10
+ join_fragments(parent_schema.uri.fragment, id)
11
+
12
+ parent_schema.uri.merge(new_uri_part).normalize
13
+ elsif parent_schema.uri.path.empty?
14
+ join_fragments(parent_schema.uri.fragment, id)
15
+ else
16
+ # RFC 3986, cl. 5.1.4
17
+ fail InvalidSchema, 'Cannot establish base URI'
18
+ end
19
+ else
20
+ uri(schema_id || id || '#')
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def join_fragments(primary, secondary)
27
+ uri('#' + File.join(primary || '', secondary || ''))
28
+ end
29
+
30
+ def uri(uri_string)
31
+ # NOTE: We need to escape % because URI class does not allow such
32
+ # characters within URI fragment (which is wrong).
33
+ URI(URI.escape(uri_string, '%'))
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,14 +2,11 @@ module JSchema
2
2
  class SimpleValidator
3
3
  class << self
4
4
  def build(schema, parent)
5
- # Validator keywords must be explicitly specified.
6
- fail UnknownError unless keywords
7
-
8
5
  args = schema.values_at(*keywords)
9
6
  new(*args, parent) unless args.compact.empty?
10
7
  end
11
8
 
12
- private
9
+ protected
13
10
 
14
11
  attr_accessor :keywords
15
12
  end
@@ -60,17 +57,20 @@ module JSchema
60
57
  end
61
58
 
62
59
  def number?(value)
63
- integer?(value) || value.is_a?(Float) || value.is_a?(BigDecimal)
60
+ value.is_a?(Numeric)
64
61
  end
65
62
 
66
- def unique_non_empty_array?(value)
67
- value.is_a?(Array) &&
68
- !value.empty? &&
69
- value.size == value.uniq.size
63
+ def non_empty_array?(value, uniqueness_check = true)
64
+ result = value.is_a?(Array) && !value.empty?
65
+ if uniqueness_check
66
+ result && value.size == value.uniq.size
67
+ else
68
+ result
69
+ end
70
70
  end
71
71
 
72
- def schema_array?(value, id)
73
- unique_non_empty_array?(value) &&
72
+ def schema_array?(value, id, uniqueness_check = true)
73
+ non_empty_array?(value, uniqueness_check) &&
74
74
  value.to_enum.with_index.all? do |schema, index|
75
75
  full_id = [id, index].join('/')
76
76
  valid_schema? schema, full_id
@@ -78,7 +78,7 @@ module JSchema
78
78
  end
79
79
 
80
80
  def valid_schema?(schema, id)
81
- schema.is_a?(Hash) && !!Schema.build(schema, parent, id)
81
+ schema.is_a?(Hash) && Schema.build(schema, parent, id)
82
82
  rescue InvalidSchema
83
83
  false
84
84
  end
@@ -23,7 +23,7 @@ module JSchema
23
23
  end
24
24
 
25
25
  def valid_property_dependency?(dependency)
26
- unique_non_empty_array?(dependency) &&
26
+ non_empty_array?(dependency) &&
27
27
  dependency.all? { |property| property.is_a?(String) }
28
28
  end
29
29
 
@@ -48,8 +48,8 @@ module JSchema
48
48
  schema = Schema.build(validator, parent, property)
49
49
  schema.validate(instance)
50
50
  when Array
51
- required = Validator::Required.new(validator)
52
- required.validate(instance)
51
+ required = Validator::Required.new(validator, nil)
52
+ Array required.validate(instance)
53
53
  else
54
54
  fail UnknownError
55
55
  end
@@ -6,7 +6,7 @@ module JSchema
6
6
  self.keywords = ['enum']
7
7
 
8
8
  def validate_args(enum)
9
- unique_non_empty_array?(enum) || invalid_schema('enum', enum)
9
+ non_empty_array?(enum) || invalid_schema('enum', enum)
10
10
  end
11
11
 
12
12
  def post_initialize(enum)
@@ -9,7 +9,7 @@ module JSchema
9
9
 
10
10
  def validate_args(format)
11
11
  allowed_formats =
12
- ['date-time', 'email', 'hostname', 'ipv4', 'ipv6', 'uri']
12
+ ['date-time', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'regex']
13
13
 
14
14
  if allowed_formats.include?(format)
15
15
  true
@@ -70,6 +70,13 @@ module JSchema
70
70
  rescue URI::InvalidURIError
71
71
  false
72
72
  end
73
+
74
+ def regex(instance)
75
+ Regexp.new(instance)
76
+ true
77
+ rescue TypeError, RegexpError
78
+ false
79
+ end
73
80
  end
74
81
  end
75
82
  end
@@ -20,12 +20,12 @@ module JSchema
20
20
  def items_valid?(items)
21
21
  items.nil? ||
22
22
  valid_schema?(items, 'items') ||
23
- schema_array?(items, 'items') ||
23
+ schema_array?(items, 'items', false) ||
24
24
  invalid_schema('items', items)
25
25
  end
26
26
 
27
27
  def post_initialize(items, additional_items)
28
- @items = items
28
+ @items = items || {}
29
29
  @additional_items = additional_items
30
30
  end
31
31
 
@@ -14,12 +14,13 @@ module JSchema
14
14
  end
15
15
 
16
16
  def post_initialize(multiple_of)
17
- @multiple_of = multiple_of
17
+ @multiple_of = BigDecimal.new(multiple_of.to_s)
18
18
  end
19
19
 
20
20
  def validate_instance(instance)
21
- div_remainder = instance.abs % @multiple_of
22
- unless div_remainder.abs < 1e-6
21
+ number = BigDecimal.new(instance.to_s)
22
+ div_remainder = number % @multiple_of
23
+ unless div_remainder == 0
23
24
  "#{instance} must be a multiple of #{@multiple_of}"
24
25
  end
25
26
  end
@@ -14,7 +14,7 @@ module JSchema
14
14
  def validate_args(pattern)
15
15
  Regexp.new(pattern)
16
16
  true
17
- rescue TypeError, PrimitiveFailure
17
+ rescue TypeError, PrimitiveFailure, RegexpError
18
18
  invalid_schema 'pattern', pattern
19
19
  end
20
20
 
@@ -32,13 +32,24 @@ module JSchema
32
32
  end
33
33
 
34
34
  def valid_properties?(properties)
35
- properties.nil? ||
36
- (properties.is_a?(Hash) && properties.all? { |_, v| v.is_a?(Hash) })
35
+ properties.nil? || properties.is_a?(Hash)
37
36
  end
38
37
 
39
38
  def post_initialize(properties, pattern_properties, additional_properties)
40
- @properties = properties
41
- @additional_properties = additional_properties
39
+ @properties =
40
+ if properties.is_a?(Hash)
41
+ properties.each_with_object({}) do |(field, sch), res|
42
+ res[field] = Schema.build(sch, parent, "properties/#{field}")
43
+ end
44
+ end
45
+
46
+ @additional_properties =
47
+ if additional_properties.is_a?(Hash)
48
+ Schema.build(additional_properties, parent, 'additionalProperties')
49
+ else
50
+ additional_properties
51
+ end
52
+
42
53
  @pattern_properties = pattern_properties
43
54
  end
44
55
 
@@ -67,26 +78,26 @@ module JSchema
67
78
  private
68
79
 
69
80
  def schemas_for(field)
70
- [
71
- properties_schema(field),
72
- additional_properties_schema(field),
73
- *pattern_properties_schema(field)
74
- ].compact
81
+ schemas = pattern_properties_schema(field)
82
+
83
+ if (psch = properties_schema(field))
84
+ schemas << psch
85
+ end
86
+
87
+ if schemas.empty? && (asch = additional_properties_schema)
88
+ schemas << asch
89
+ end
90
+
91
+ schemas
75
92
  end
76
93
 
77
94
  def properties_schema(field)
78
- if @properties.is_a?(Hash)
79
- if (sch = @properties[field])
80
- Schema.build(sch, parent, 'properties')
81
- end
82
- end
95
+ @properties[field] if @properties.is_a?(Hash)
83
96
  end
84
97
 
85
- def additional_properties_schema(field)
86
- if @additional_properties.is_a?(Hash)
87
- if (sch = @additional_properties[field])
88
- Schema.build(sch, parent, 'additionalProperties')
89
- end
98
+ def additional_properties_schema
99
+ if @additional_properties.respond_to?(:validate)
100
+ @additional_properties
90
101
  end
91
102
  end
92
103
 
@@ -95,7 +106,7 @@ module JSchema
95
106
  if @pattern_properties.is_a?(Hash)
96
107
  @pattern_properties.each do |pattern, sch|
97
108
  if field.match(pattern)
98
- schemas << Schema.build(sch, parent, 'patternProperties')
109
+ schemas << Schema.build(sch, parent, "patternProperties/#{field}")
99
110
  end
100
111
  end
101
112
  end
@@ -19,14 +19,14 @@ module JSchema
19
19
 
20
20
  def validate_instance(instance)
21
21
  @required.each do |required_property|
22
- if instance[required_property].nil?
22
+ unless instance.key?(required_property)
23
23
  return "#{instance} must have property `#{required_property}`"
24
24
  end
25
25
  end and nil
26
26
  end
27
27
 
28
28
  def valid_required?(required)
29
- unique_non_empty_array?(required) &&
29
+ non_empty_array?(required) &&
30
30
  required.all? { |req| req.is_a?(String) }
31
31
  end
32
32
  end
@@ -6,7 +6,7 @@ module JSchema
6
6
  self.keywords = ['type']
7
7
 
8
8
  def validate_args(type)
9
- if type.is_a?(String) || unique_non_empty_array?(type)
9
+ if type.is_a?(String) || non_empty_array?(type)
10
10
  true
11
11
  else
12
12
  invalid_schema 'type', type
@@ -9,7 +9,7 @@ class TestJSONReference < Minitest::Test
9
9
  def test_schema_registration_and_dereferencing
10
10
  schema = generate_schema('registered')
11
11
  JSchema::JSONReference.register_schema schema
12
- assert_equal dereference(schema), schema
12
+ assert_equal schema, dereference(schema)
13
13
  end
14
14
 
15
15
  def test_schema_dereferencing_with_same_uri
@@ -18,8 +18,8 @@ class TestJSONReference < Minitest::Test
18
18
  JSchema::JSONReference.register_schema schema1
19
19
  JSchema::JSONReference.register_schema schema2
20
20
 
21
- assert_equal dereference(schema1), schema1
22
- assert_equal dereference(schema2), schema2
21
+ assert_equal schema1, dereference(schema1)
22
+ assert_equal schema2, dereference(schema2)
23
23
  end
24
24
 
25
25
  def test_dereferencing_external_schema
@@ -29,7 +29,7 @@ class TestJSONReference < Minitest::Test
29
29
  expected_schema_args = [external_schema, URI(schema_uri), nil]
30
30
  assert_received JSchema::Schema, :new, expected_schema_args do
31
31
  JSchema::JSONReference.stub :register_schema, nil do
32
- dereference_external_schema schema_uri, external_schema.to_json
32
+ dereference_external_schema schema_uri, external_schema.to_json, false
33
33
  end
34
34
  end
35
35
  end
@@ -62,19 +62,50 @@ class TestJSONReference < Minitest::Test
62
62
  end
63
63
  end
64
64
 
65
+ def test_dereferencing_within_schema_with_non_default_uri
66
+ schema = generate_schema('http://example.com/', false)
67
+ JSchema::JSONReference.register_schema schema
68
+ assert_equal schema, JSchema::JSONReference.dereference(URI('#'), schema)
69
+ end
70
+
71
+ def test_dereferencing_root_schema
72
+ schema1 = generate_schema('#', false)
73
+ schema2 = generate_schema('http://example.com/', false)
74
+ JSchema::JSONReference.register_schema schema1
75
+ JSchema::JSONReference.register_schema schema2
76
+
77
+ assert_equal schema2,
78
+ JSchema::JSONReference.dereference(URI('#'), schema2)
79
+
80
+ assert_equal schema2,
81
+ JSchema::JSONReference.dereference(URI('http://example.com/#'), schema2)
82
+
83
+ assert_equal schema2,
84
+ JSchema::JSONReference.dereference(URI('http://example.com/'), schema2)
85
+ end
86
+
87
+ def test_dereferencing_schema_by_slightly_different_uri
88
+ schema = generate_schema('http://example.com/#', false)
89
+ JSchema::JSONReference.register_schema schema
90
+
91
+ assert_equal schema,
92
+ JSchema::JSONReference.dereference(URI('http://example.com/'), schema)
93
+ end
94
+
65
95
  private
66
96
 
67
- def dereference_external_schema(uri, response_schema)
97
+ def dereference_external_schema(uri, response_schema, parent = true)
68
98
  stub_request(:get, uri).to_return(body: response_schema)
69
- dereference generate_schema(uri)
99
+ dereference generate_schema(uri, parent)
70
100
  end
71
101
 
72
102
  def dereference(schema)
73
- JSchema::JSONReference.dereference(schema.uri, schema)
103
+ JSchema::JSONReference.dereference(schema.uri, schema.parent)
74
104
  end
75
105
 
76
- def generate_schema(uri)
77
- OpenStruct.new(uri: URI(uri), id: generate_id)
106
+ def generate_schema(uri, parent = true)
107
+ parent_schema = generate_schema('#', false) if parent
108
+ OpenStruct.new(uri: URI(uri), id: generate_id, parent: parent_schema)
78
109
  end
79
110
 
80
111
  def generate_id
@@ -1,4 +1,5 @@
1
1
  require 'minitest/autorun'
2
+ require 'webmock/minitest'
2
3
  require 'jschema'
3
4
 
4
5
  require_relative 'assert_received'
@@ -25,41 +26,6 @@ class TestSchema < Minitest::Test
25
26
  end
26
27
  end
27
28
 
28
- def test_default_schema_uri
29
- assert_equal URI('#'), schema_uri
30
- end
31
-
32
- def test_schema_uri_when_parent_is_not_specified
33
- assert_equal URI('test'), schema_uri('test')
34
- end
35
-
36
- def test_schema_uri_when_parent_uri_is_absolute
37
- uri = schema_uri('test', 'http://example.com/')
38
- assert_equal URI('http://example.com/test'), uri
39
- end
40
-
41
- def test_schema_uri_when_parent_uri_is_relative
42
- assert_raises(JSchema::InvalidSchema) do
43
- schema_uri('relative/', 'relative/')
44
- end
45
- end
46
-
47
- def test_schema_uri_when_both_parent_and_schema_uri_are_absolute
48
- schema_id = 'http://example.com/'
49
- parent_id = 'http://localhost/'
50
- uri = schema_uri(schema_id, parent_id)
51
- assert_equal URI(schema_id), uri
52
- end
53
-
54
- def test_schema_uri_when_ids_are_not_specified
55
- assert_equal URI('#/child'), schema_uri(nil, nil, 'child')
56
- end
57
-
58
- def test_that_schema_uri_is_normalized
59
- uri = schema_uri('etc/../path', 'http://Example.com')
60
- assert_equal 'http://example.com/path', uri.to_s
61
- end
62
-
63
29
  def test_json_refenrece
64
30
  schema = Object.new
65
31
  JSchema::SchemaRef.stub :new, schema do
@@ -68,6 +34,20 @@ class TestSchema < Minitest::Test
68
34
  end
69
35
  end
70
36
 
37
+ def test_that_tilda_is_unescaped
38
+ expected_ref_uri = URI('#/definitions/sch~')
39
+ assert_received JSchema::SchemaRef, :new, [expected_ref_uri, nil] do
40
+ JSchema::Schema.build('$ref' => '#/definitions/sch~0')
41
+ end
42
+ end
43
+
44
+ def test_that_forward_slash_is_unescaped
45
+ expected_ref_uri = URI('#/definitions/sch/sch')
46
+ assert_received JSchema::SchemaRef, :new, [expected_ref_uri, nil] do
47
+ JSchema::Schema.build('$ref' => '#/definitions/sch~1sch')
48
+ end
49
+ end
50
+
71
51
  def test_storing_schema_in_registry
72
52
  sch = Object.new
73
53
  JSchema::Schema.stub :new, sch do
@@ -77,12 +57,29 @@ class TestSchema < Minitest::Test
77
57
  end
78
58
  end
79
59
 
60
+ # TODO: Make it isolated.
61
+ def test_schema_caching
62
+ parent = JSchema::Schema.build
63
+ sch = { 'type' => 'string' }
64
+ schema1 = JSchema::Schema.build(sch, parent)
65
+ schema2 = JSchema::Schema.build(sch, parent)
66
+ assert_equal schema1.object_id, schema2.object_id
67
+ end
68
+
69
+ # TODO: Make it isolated.
70
+ def test_that_root_schemas_are_not_cached
71
+ sch = { 'type' => 'string' }
72
+ schema1 = JSchema::Schema.build(sch)
73
+ schema2 = JSchema::Schema.build(sch)
74
+ refute_equal schema1.object_id, schema2.object_id
75
+ end
76
+
80
77
  # TODO: Make it isolated.
81
78
  def test_definitions
82
- schema_def_uri = '#/definitions/schema1'
79
+ schema_def_uri = URI('#/definitions/schema1')
83
80
  schema = JSchema::Schema.build('definitions' => { 'schema1' => {} })
84
81
  definition = JSchema::SchemaRef.new(schema_def_uri, schema)
85
- assert_equal URI(schema_def_uri), definition.uri
82
+ assert_equal schema_def_uri, definition.uri
86
83
  end
87
84
 
88
85
  def test_that_exception_is_raised_when_schema_version_is_not_supported
@@ -8,4 +8,12 @@ class TestSchemaRef < Minitest::Test
8
8
  assert_equal JSchema::SchemaRef.new(schema.uri, nil), schema
9
9
  end
10
10
  end
11
+
12
+ def test_that_exception_is_raised_if_reference_is_incorrect
13
+ JSchema::JSONReference.stub :dereference, nil do
14
+ assert_raises(JSchema::InvalidSchema) do
15
+ JSchema::SchemaRef.new('invalid', nil).parent
16
+ end
17
+ end
18
+ end
11
19
  end
@@ -0,0 +1,56 @@
1
+ require 'minitest/autorun'
2
+ require 'jschema'
3
+
4
+ class TestSchemaURI < Minitest::Test
5
+ def test_default_uri
6
+ assert_equal URI('#'), schema_uri
7
+ end
8
+
9
+ def test_schema_uri_when_parent_is_not_specified
10
+ assert_equal URI('test'), schema_uri('test')
11
+ end
12
+
13
+ def test_schema_uri_when_parent_uri_is_absolute
14
+ uri = schema_uri('test', 'http://example.com/')
15
+ assert_equal URI('http://example.com/test'), uri
16
+ end
17
+
18
+ def test_schema_uri_when_parent_uri_is_relative
19
+ assert_raises(JSchema::InvalidSchema) do
20
+ schema_uri('relative/', 'relative/')
21
+ end
22
+ end
23
+
24
+ def test_schema_uri_when_both_parent_and_schema_uri_are_absolute
25
+ schema_id = 'http://example.com/'
26
+ parent_id = 'http://localhost/'
27
+ uri = schema_uri(schema_id, parent_id)
28
+ assert_equal URI(schema_id), uri
29
+ end
30
+
31
+ def test_schema_uri_when_ids_are_not_specified
32
+ assert_equal URI('#/child'), schema_uri(nil, nil, 'child')
33
+ end
34
+
35
+ def test_that_schema_uri_is_normalized
36
+ uri = schema_uri('etc/../path', 'http://Example.com')
37
+ assert_equal URI('http://example.com/path'), uri
38
+ end
39
+
40
+ def test_that_implicit_schema_id_treated_as_uri_fragment
41
+ uri = schema_uri(nil, 'http://example.com/path#sch', 'sub')
42
+ assert_equal URI('http://example.com/path#sch/sub'), uri
43
+ end
44
+
45
+ def test_escaping
46
+ uri = schema_uri(nil, '#/definitions', 'schema%')
47
+ assert_equal URI('#/definitions/schema%25'), uri
48
+ end
49
+
50
+ private
51
+
52
+ def schema_uri(schema_id = nil, parent_id = nil, id = nil)
53
+ parent_schema = JSchema::Schema.build('id' => parent_id) if parent_id || id
54
+ JSchema::SchemaURI.build(schema_id, parent_schema, id)
55
+ end
56
+ end
@@ -94,7 +94,7 @@ class TestDependencies < Minitest::Test
94
94
  validator_stub =
95
95
  Struct.new(:valid) do
96
96
  def validate(_)
97
- valid ? [] : ['error']
97
+ 'error' unless valid
98
98
  end
99
99
  end
100
100
 
@@ -62,6 +62,14 @@ class TestFormat < Minitest::Test
62
62
  refute validator('uri').valid?('://')
63
63
  end
64
64
 
65
+ def test_passing_validation_by_regex
66
+ assert validator('regex').valid?('\d+')
67
+ end
68
+
69
+ def test_failing_validation_by_regex
70
+ refute validator('regex').valid?('**')
71
+ end
72
+
65
73
  private
66
74
 
67
75
  def validator_class
@@ -28,6 +28,11 @@ class TestItems < Minitest::Test
28
28
  assert_raises_unless_schema_array 'items'
29
29
  end
30
30
 
31
+ def test_default_value_for_items
32
+ validator = build_from_schema('additionalItems' => false)
33
+ assert validator.valid? [1, 2, 3]
34
+ end
35
+
31
36
  def test_passing_validation_by_items_schema
32
37
  stub_schema_validations(true) do
33
38
  assert build_from_schema('items' => {}).valid?(['test'])
@@ -35,8 +40,9 @@ class TestItems < Minitest::Test
35
40
  end
36
41
 
37
42
  def test_passing_validation_by_items_schema_array
43
+ basic_schema = generate_schema
38
44
  stub_schema_validations(true) do
39
- schema = { 'items' => [generate_schema, generate_schema] }
45
+ schema = { 'items' => [basic_schema, basic_schema] }
40
46
  assert build_from_schema(schema).valid?(['test', 'test'])
41
47
  end
42
48
  end
@@ -17,14 +17,6 @@ class TestProperties < Minitest::Test
17
17
  end
18
18
  end
19
19
 
20
- def test_invalid_properties_schema_argument
21
- keywords.each do |keyword|
22
- assert_raises(JSchema::InvalidSchema) do
23
- build_from_schema(keyword => { 'test' => 1 })
24
- end
25
- end
26
- end
27
-
28
20
  def test_passing_properties_validation_by_sub_schema
29
21
  keywords.each do |keyword|
30
22
  assert_passing_validation keyword
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jschema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Papkovskiy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-29 00:00:00.000000000 Z
11
+ date: 2014-08-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Implementation of JSON Schema Draft 4
14
14
  email: konstantin@papkovskiy.com
@@ -18,8 +18,10 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - lib/jschema.rb
20
20
  - lib/jschema/json_reference.rb
21
+ - lib/jschema/method_call_logger.rb
21
22
  - lib/jschema/schema.rb
22
23
  - lib/jschema/schema_ref.rb
24
+ - lib/jschema/schema_uri.rb
23
25
  - lib/jschema/simple_validator.rb
24
26
  - lib/jschema/string_length_validator.rb
25
27
  - lib/jschema/validator.rb
@@ -52,6 +54,7 @@ files:
52
54
  - test/test_json_reference.rb
53
55
  - test/test_schema.rb
54
56
  - test/test_schema_ref.rb
57
+ - test/test_schema_uri.rb
55
58
  - test/test_validator.rb
56
59
  - test/validator/argument_is_array_of_schemas_tests.rb
57
60
  - test/validator/assertions.rb
@@ -101,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
104
  version: '0'
102
105
  requirements: []
103
106
  rubyforge_project:
104
- rubygems_version: 2.2.0
107
+ rubygems_version: 2.2.2
105
108
  signing_key:
106
109
  specification_version: 4
107
110
  summary: JSON Schema implementation
@@ -113,6 +116,7 @@ test_files:
113
116
  - test/test_json_reference.rb
114
117
  - test/test_schema.rb
115
118
  - test/test_schema_ref.rb
119
+ - test/test_schema_uri.rb
116
120
  - test/test_validator.rb
117
121
  - test/validator/argument_is_array_of_schemas_tests.rb
118
122
  - test/validator/assertions.rb