json_schema_tools 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG.md +5 -1
- data/lib/schema_tools/ref_resolver.rb +18 -6
- data/lib/schema_tools/schema.rb +42 -16
- data/lib/schema_tools/version.rb +1 -1
- data/spec/fixtures/circular_references_twice.json +10 -0
- data/spec/fixtures_broken/circular_references.json +9 -0
- data/spec/fixtures_broken/circular_references_multi.json +14 -0
- data/spec/fixtures_broken/circular_references_multi_file_one.json +14 -0
- data/spec/fixtures_broken/circular_references_multi_file_two.json +14 -0
- data/spec/schema_tools/circuluar_ref_spec.rb +28 -0
- data/spec/schema_tools/reader_spec.rb +1 -0
- data/spec/schema_tools/ref_resolver_spec.rb +0 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OWMzMmM0YmMzMDY4YmVhMjQ4MjI4Nzk1MGQwNTVjYzFiYTNlM2Q0ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NDU3MjkxYTUxNjNlMWNkOGE5MGJkZGNkOTUxOGE3OThjMDM3NDIzNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDBhMzllODdmMzgxOWQ3OWExM2IzZjU4YjIwYWZhODgyMzk5ZDU3NjUwNmY1
|
10
|
+
Mjg3ODcxZjEzMGE3N2JkODY1ZmMxOWI1MDFkMDVlODc0ODRlNDU2Mjc4NTAw
|
11
|
+
OGQ0NDQxOThkZGNkZjliMTYwMGY2ZDQ4ZmY2NDM3NjQ2YjU0YmI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTNkOTU5YmU0OGQzNDMyYzAyZWQ1Mjc3ZGRkNWIzOGQ0MjU0NzcxOGRkODNj
|
14
|
+
NTcxNzEwZDAxNTAxMTk5Mzk4OGM1Y2U3NWEwMWY0NmY1NjZhZTQyNTA3ODYx
|
15
|
+
NDU1NWUxYzYzZWU4NTkyNjgwNzYwYzZjZDVmZGFkMjNjNWQyNjc=
|
data/CHANGELOG.md
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
# Changelog JSON Schema Tools
|
2
2
|
|
3
3
|
|
4
|
+
2014-10
|
5
|
+
|
6
|
+
* handle circular dependencies in $ref arguments, so schema.to_h always resolves $refs properly
|
7
|
+
|
4
8
|
2014-09
|
5
9
|
|
6
10
|
* add Schema class to represent a single schema, replaces simple Hash usage. Potentially breaks usage if you've gone crazy with Hash methods in your client.
|
7
11
|
|
8
12
|
2014-09
|
9
13
|
|
10
|
-
*
|
14
|
+
* refactor Reader to de-reference all pointers when initialized
|
11
15
|
* remove :exclude_root option for object to schema_hash BREAKING change if you want nesting define your schema accordingly
|
12
16
|
* add :links option for object to schema_hash, to include the links inline in an object
|
13
17
|
* support items definition for array type properties - BREAKING you must change old simple property definitions
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'uri'
|
4
5
|
|
5
6
|
module SchemaTools
|
6
7
|
class RefResolver
|
@@ -13,7 +14,7 @@ module SchemaTools
|
|
13
14
|
# http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
|
14
15
|
#
|
15
16
|
# @param [String] json_pointer the JSON Pointer expression to evaluate
|
16
|
-
# @param [
|
17
|
+
# @param [Schema] relative_to if the pointer refers to a local schema, this is this
|
17
18
|
# the hash to evaluate it against. If the pointer contains a uri to a
|
18
19
|
# referenced schema, an attempt is made to load
|
19
20
|
def self.load_json_pointer json_pointer, relative_to = nil
|
@@ -26,14 +27,25 @@ module SchemaTools
|
|
26
27
|
uri = $1.strip
|
27
28
|
pointer = $2
|
28
29
|
schema = {}
|
29
|
-
|
30
|
+
unless uri.empty?
|
30
31
|
uri = URI.parse(uri)
|
31
32
|
raise "must currently be a relative uri: #{json_pointer}" if uri.absolute?
|
32
33
|
# TODO use locale tools instance or base path from global SchemaTools.schema_path
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
base_dir = relative_to ? relative_to.absolute_dir : SchemaTools.schema_path
|
35
|
+
path = File.join(base_dir, uri.path)
|
36
|
+
unless File.exist?(path)
|
37
|
+
# try to find in main-dir and subdirs of global schema path and if
|
38
|
+
# present a schema's absolute dir
|
39
|
+
filename = uri.path.split('/').last
|
40
|
+
search_dirs = [File.join(SchemaTools.schema_path, filename),
|
41
|
+
File.join(SchemaTools.schema_path, '**/*', filename)]
|
42
|
+
if relative_to
|
43
|
+
search_dirs += [ File.join(relative_to.absolute_dir, filename),
|
44
|
+
File.join(relative_to.absolute_dir, '**/*', filename) ]
|
45
|
+
end
|
46
|
+
recursive_search = Dir.glob(search_dirs)[0]
|
47
|
+
# if still not found keep orig path to throw error on open
|
48
|
+
path = recursive_search || path
|
37
49
|
end
|
38
50
|
open (path) {|f| schema = JSON.parse(f.read) }
|
39
51
|
end
|
data/lib/schema_tools/schema.rb
CHANGED
@@ -1,10 +1,20 @@
|
|
1
|
+
module SchemaTools
|
1
2
|
|
3
|
+
class CircularReferenceException < ::Exception
|
4
|
+
def initialize fn, offending_ref, stack
|
5
|
+
@fn = fn
|
6
|
+
@stack = stack
|
7
|
+
@offending_ref = offending_ref
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"circular reference: #{@fn} $ref: #{@offending_ref} refers to itself:\n #{@stack.join("\n")}"
|
12
|
+
end
|
13
|
+
end
|
2
14
|
|
3
|
-
module SchemaTools
|
4
15
|
# Internal representation of a Schema. This is basically a wrapper around a
|
5
16
|
# HashWithIndifferentAccess ( for historical purposes ) as well as information
|
6
17
|
# concerning where the Schema was loaded from in order to resolve relative paths.
|
7
|
-
|
8
18
|
class Schema
|
9
19
|
#@param [String|Hash] name_or_hash Schema may be initialized with either a filename or a hash
|
10
20
|
def initialize(name_or_hash)
|
@@ -115,31 +125,47 @@ module SchemaTools
|
|
115
125
|
# "$ref" param and resolve it. Other params are checked for nested hashes
|
116
126
|
# and those are processed.
|
117
127
|
# @param [HashWithIndifferentAccess] schema - single schema
|
118
|
-
def resolve_refs schema = nil
|
128
|
+
def resolve_refs schema = nil, stack = []
|
119
129
|
schema ||= @hash
|
120
|
-
|
121
|
-
def resolve_reference hash
|
122
|
-
json_pointer = hash["$ref"]
|
123
|
-
values_from_pointer = RefResolver.load_json_pointer json_pointer, self
|
124
|
-
hash.merge!(values_from_pointer) { |key, old, new| old }
|
125
|
-
hash.delete("$ref")
|
126
|
-
end
|
127
|
-
|
128
130
|
keys = schema.keys # in case you are wondering: RuntimeError: can't add a new key into hash during iteration
|
129
131
|
keys.each do |k|
|
130
132
|
v = schema[k]
|
131
133
|
if k == "$ref"
|
132
|
-
resolve_reference schema
|
133
|
-
|
134
|
-
|
134
|
+
resolve_reference schema, stack
|
135
|
+
#stack.clear # ref resolved, reset stack
|
136
|
+
elsif v.is_a?(::Hash) || v.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
137
|
+
resolve_refs v, stack
|
135
138
|
elsif v.is_a?(Array)
|
136
|
-
v.each do |
|
137
|
-
|
139
|
+
v.each do |element|
|
140
|
+
case element
|
141
|
+
when ::Hash, ActiveSupport::HashWithIndifferentAccess, ::Array
|
142
|
+
resolve_refs element, stack
|
143
|
+
end
|
138
144
|
end
|
139
145
|
end
|
140
146
|
end
|
147
|
+
schema
|
148
|
+
end
|
141
149
|
|
150
|
+
#
|
151
|
+
# @param [Hash] hash schema
|
152
|
+
def resolve_reference(hash, stack=[])
|
153
|
+
json_pointer = hash["$ref"]
|
154
|
+
if stack.include? json_pointer
|
155
|
+
# we should probably also have a "too many levels of $ref exception or something ..."
|
156
|
+
raise CircularReferenceException.new( absolute_filename, json_pointer, stack )
|
157
|
+
else
|
158
|
+
stack.push json_pointer
|
159
|
+
end
|
160
|
+
values_from_pointer = RefResolver.load_json_pointer(json_pointer, self)
|
161
|
+
# recurse to resolve possible refs in object properties
|
162
|
+
resolve_refs(values_from_pointer, stack)
|
163
|
+
|
164
|
+
hash.merge!(values_from_pointer) { |key, old, new| old }
|
165
|
+
hash.delete("$ref")
|
166
|
+
stack.pop
|
142
167
|
end
|
168
|
+
|
143
169
|
end
|
144
170
|
end
|
145
171
|
|
data/lib/schema_tools/version.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"type" : "object",
|
3
|
+
"one": {
|
4
|
+
"$ref":"./circular_references_multi.json#two"
|
5
|
+
},
|
6
|
+
"two": {
|
7
|
+
"$ref":"./circular_references_multi.json#three"
|
8
|
+
},
|
9
|
+
"three" : {
|
10
|
+
"something_else" : {
|
11
|
+
"$ref": "./circular_references_multi.json#one"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"type" : "object",
|
3
|
+
"one": {
|
4
|
+
"$ref":"./circular_references_multi_file_two.json#one"
|
5
|
+
},
|
6
|
+
"two": {
|
7
|
+
"$ref":"./circular_references_multi_file_two.json#two"
|
8
|
+
},
|
9
|
+
"three" : {
|
10
|
+
"something_else" : {
|
11
|
+
"$ref": "./circular_references_multi_file_two.json#three"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"type" : "object",
|
3
|
+
"one": {
|
4
|
+
"$ref":"./circular_references_multi_file_one.json#two"
|
5
|
+
},
|
6
|
+
"two": {
|
7
|
+
"$ref":"./circular_references_multi_file_one.json#three"
|
8
|
+
},
|
9
|
+
"three" : {
|
10
|
+
"something_else" : {
|
11
|
+
"$ref": "./circular_references_multi_file_one.json#one"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SchemaTools::Reader do
|
4
|
+
BROKEN_SCHEMA_PATH = File.expand_path('../../fixtures_broken', __FILE__)
|
5
|
+
context 'circular references' do
|
6
|
+
it 'should raise exception for circular $refs' do
|
7
|
+
expect{
|
8
|
+
schema = SchemaTools::Schema.new("#{BROKEN_SCHEMA_PATH}/circular_references.json")
|
9
|
+
}.to raise_exception(SchemaTools::CircularReferenceException)
|
10
|
+
end
|
11
|
+
it 'should raise exception for more complex circular $refs' do
|
12
|
+
expect{
|
13
|
+
schema = SchemaTools::Schema.new("#{BROKEN_SCHEMA_PATH}/circular_references_multi.json")
|
14
|
+
}.to raise_exception(SchemaTools::CircularReferenceException)
|
15
|
+
end
|
16
|
+
it 'should raise exception for way multi file circular $refs' do
|
17
|
+
expect {
|
18
|
+
schema = SchemaTools::Schema.new("#{BROKEN_SCHEMA_PATH}/circular_references_multi_file_one.json")
|
19
|
+
}.to raise_exception
|
20
|
+
end
|
21
|
+
it 'should not raise exception if same $ref is used twice in one file' do
|
22
|
+
expect {
|
23
|
+
SchemaTools::Reader.read(:circular_references_twice)
|
24
|
+
}.to_not raise_exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_schema_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Georg Leciejewski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- lib/schema_tools/version.rb
|
99
99
|
- spec/fixtures/address.json
|
100
100
|
- spec/fixtures/basic_definitions.json
|
101
|
+
- spec/fixtures/circular_references_twice.json
|
101
102
|
- spec/fixtures/client.json
|
102
103
|
- spec/fixtures/contact.json
|
103
104
|
- spec/fixtures/includes_basic_definitions.json
|
@@ -107,6 +108,11 @@ files:
|
|
107
108
|
- spec/fixtures/one_of_definition.json
|
108
109
|
- spec/fixtures/page.json
|
109
110
|
- spec/fixtures/relative_paths/includes_relative_path.json
|
111
|
+
- spec/fixtures_broken/circular_references.json
|
112
|
+
- spec/fixtures_broken/circular_references_multi.json
|
113
|
+
- spec/fixtures_broken/circular_references_multi_file_one.json
|
114
|
+
- spec/fixtures_broken/circular_references_multi_file_two.json
|
115
|
+
- spec/schema_tools/circuluar_ref_spec.rb
|
110
116
|
- spec/schema_tools/cleaner_spec.rb
|
111
117
|
- spec/schema_tools/hash_spec.rb
|
112
118
|
- spec/schema_tools/klass_factory_spec.rb
|