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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZjMyMjgzZTgyMzQ3OTBlOGVlNDEzNTI0OTMwYWQxNGFkYTViNzMwMA==
4
+ OWMzMmM0YmMzMDY4YmVhMjQ4MjI4Nzk1MGQwNTVjYzFiYTNlM2Q0ZQ==
5
5
  data.tar.gz: !binary |-
6
- YTE5ODAyMTI0YThkYzc1ZDFhYjFiMDA2ODVhZDJjZDI2MjRkMDAwNw==
6
+ NDU3MjkxYTUxNjNlMWNkOGE5MGJkZGNkOTUxOGE3OThjMDM3NDIzNQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- Y2YwOWZhMWEyMmZlZDdjZjlmMDA5NDhkMDNiM2RmZTJhYWIwNjM2ZDM4NGQy
10
- OGE4ZGNhNjg2OGUwY2FkOWFkNmRhMWRkNzAyN2JkY2VmMmJhODliNTMxZWUz
11
- ZWI0MWRmYTQ1ZTEzYWM5NzRmMGY0OWRjZTJhZTVhMzUyMzgxOWY=
9
+ NDBhMzllODdmMzgxOWQ3OWExM2IzZjU4YjIwYWZhODgyMzk5ZDU3NjUwNmY1
10
+ Mjg3ODcxZjEzMGE3N2JkODY1ZmMxOWI1MDFkMDVlODc0ODRlNDU2Mjc4NTAw
11
+ OGQ0NDQxOThkZGNkZjliMTYwMGY2ZDQ4ZmY2NDM3NjQ2YjU0YmI=
12
12
  data.tar.gz: !binary |-
13
- MjRiYWQ5MTJkNmU2OGM3NGI0ODlkYzFjMzAzMjgyMzFmNzYzNmJjYjkwNjU1
14
- NzM1ODM4NjNlZGE5Yjg2ZDNmNzIyNGZjOTkwMjE5NzQzMTBlN2IyZTI5YTA3
15
- MThmODY2ZThiYjA4YzAxZDM4Mzk4NjJlY2ViMjdjM2EyZTY4YmQ=
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
- * refacture Reader to de-reference all pointers when initialized
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 [Hash] schema if the pointer refers to a local schema, this is this
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
- if ! uri.empty?
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
- if relative_to
34
- path = relative_to.absolute_dir + "/" + uri.path
35
- else
36
- path = SchemaTools.schema_path + "/" + uri.path
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
@@ -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
- elsif v.is_a?(ActiveSupport::HashWithIndifferentAccess)
134
- resolve_refs v
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 |i|
137
- resolve_refs(i) if i.is_a?(ActiveSupport::HashWithIndifferentAccess)
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
 
@@ -1,3 +1,3 @@
1
1
  module SchemaTools
2
- VERSION = '0.3.2'
2
+ VERSION = '0.3.3'
3
3
  end
@@ -0,0 +1,10 @@
1
+ {
2
+ "description" : "original impl borked when including the same ref twice in one file",
3
+ "type" : "object",
4
+ "one": {
5
+ "$ref":"./address.json#properties"
6
+ },
7
+ "two": {
8
+ "$ref":"./address.json#properties"
9
+ }
10
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "type" : "object",
3
+ "one": {
4
+ "$ref":"./circular_references.json#two"
5
+ },
6
+ "two": {
7
+ "$ref":"./circular_references.json#one"
8
+ }
9
+ }
@@ -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
+
@@ -112,5 +112,6 @@ describe SchemaTools::Reader do
112
112
  expect { reader.read(:client, []) }.to raise_error ArgumentError
113
113
  end
114
114
  end
115
+
115
116
  end
116
117
 
@@ -48,7 +48,6 @@ describe SchemaTools::RefResolver do
48
48
  found.should eq 2
49
49
  end
50
50
  end
51
-
52
51
  it 'should throw an exception on an invalid path' do
53
52
  obj = {}
54
53
  expect {
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.2
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-16 00:00:00.000000000 Z
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