json_schema_tools 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.gitignore +6 -0
- data/CHANGELOG.md +16 -0
- data/README.md +14 -0
- data/lib/schema_tools.rb +2 -0
- data/lib/schema_tools/modules/attributes.rb +6 -3
- data/lib/schema_tools/modules/hash.rb +47 -40
- data/lib/schema_tools/modules/read.rb +4 -32
- data/lib/schema_tools/ref_resolver.rb +9 -6
- data/lib/schema_tools/schema.rb +139 -0
- data/lib/schema_tools/version.rb +1 -1
- data/spec/fixtures/basic_definitions.json +7 -0
- data/spec/fixtures/client.json +6 -1
- data/spec/fixtures/contact.json +4 -0
- data/spec/fixtures/includes_deep_nested_refs.json +21 -0
- data/spec/fixtures/lead.json +1 -1
- data/spec/fixtures/page.json +8 -4
- data/spec/fixtures/relative_paths/includes_relative_path.json +6 -0
- data/spec/schema_tools/hash_spec.rb +22 -42
- data/spec/schema_tools/modules/as_schema_spec.rb +12 -17
- data/spec/schema_tools/reader_spec.rb +5 -0
- data/spec/schema_tools/ref_resolver_spec.rb +7 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmNjZDJjNDRjZTMyNjFjM2U0ZjFhMTRkMTJhZGNkNWVjYmVhZmU2MQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzI1OWY0OTU1ZDJmMDVmOTMyMTgzZTQ5ZjE3YjRmNDFjMzJmYzBjYg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTFmODcwMmM2MDBmZmU0YWZlYjIyODMyNjlkM2EyZWY1NTliZWI1YjQwYTU0
|
10
|
+
NGNlYTI1MmE1Y2FlMWY5ZGIwOGQwNzI2OTc5OTNlYTliMDIxMTZkZGQ2ODlj
|
11
|
+
N2NiNjFlNWQ0ZGVmM2E3M2M1NTZiM2U1NzM2ZjNmMmE2MGQ1MjM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MmMzNGU5NjkxYWQ1NzFkYzkzODcxNzI2NzY3ZTAwODE0OGY1YWUyOTY3ZTE1
|
14
|
+
ZGNjMGFhNGQ5YjU0YzNhZDJjYWIxNWUwN2M2NTNjMDZiMDI4ZmUwMTI0Njc1
|
15
|
+
ZDcyMzkzOTQ5Y2MzNzUwOTViODNkYjE5ZDIzNjY1NTJhNmFjMmI=
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Changelog JSON Schema Tools
|
2
2
|
|
3
3
|
|
4
|
+
2014-09
|
5
|
+
|
6
|
+
* 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
|
+
|
8
|
+
2014-09
|
9
|
+
|
10
|
+
* refacture Reader to de-reference all pointers when initialized
|
11
|
+
* remove :exclude_root option for object to schema_hash BREAKING change if you want nesting define your schema accordingly
|
12
|
+
* add :links option for object to schema_hash, to include the links inline in an object
|
13
|
+
* support items definition for array type properties - BREAKING you must change old simple property definitions
|
14
|
+
* support oneOf definitions
|
15
|
+
|
16
|
+
2014-08
|
17
|
+
|
18
|
+
* add $ref resolver to merge property definitions from another file
|
19
|
+
|
4
20
|
2013-10
|
5
21
|
* allow all object properties in link href placeholders => contacts/{id}/{number}
|
6
22
|
* add base_url option to schema hash creation. Prepends an url to all links of a rendered object
|
data/README.md
CHANGED
@@ -245,6 +245,20 @@ reader = SchemaTools::Reader.new
|
|
245
245
|
SchemaTools::KlassFactory.build reader: reader, path: HappyPdf::Schema.path
|
246
246
|
```
|
247
247
|
|
248
|
+
## $ref
|
249
|
+
|
250
|
+
Provides some basic support for JSON Pointer. JSON Pointer expressions must reference local
|
251
|
+
files and must contain a fragment identifier, i.e.
|
252
|
+
|
253
|
+
./some_include.json#properties
|
254
|
+
|
255
|
+
is a resolvable pointer, while
|
256
|
+
|
257
|
+
http://example.com/public_schema.json
|
258
|
+
|
259
|
+
is not.
|
260
|
+
|
261
|
+
|
248
262
|
## Real world examples
|
249
263
|
|
250
264
|
* [DocTag ruby gem](https://github.com/docTag/doctag_rb) and [DocTag json-schema](https://github.com/docTag/doctag_json_schema)
|
data/lib/schema_tools.rb
CHANGED
@@ -5,6 +5,7 @@ require 'schema_tools/modules/hash'
|
|
5
5
|
require 'schema_tools/modules/as_schema'
|
6
6
|
require 'schema_tools/modules/attributes'
|
7
7
|
require 'schema_tools/modules/validations'
|
8
|
+
require 'schema_tools/schema'
|
8
9
|
require 'schema_tools/reader'
|
9
10
|
require 'schema_tools/cleaner'
|
10
11
|
require 'schema_tools/hash'
|
@@ -13,6 +14,7 @@ require 'schema_tools/ref_resolver'
|
|
13
14
|
|
14
15
|
|
15
16
|
module SchemaTools
|
17
|
+
SCHEMA_BASE_TYPES = %w(string number integer boolean)
|
16
18
|
class << self
|
17
19
|
|
18
20
|
# @param [String] path to schema json files
|
@@ -34,7 +34,8 @@ module SchemaTools
|
|
34
34
|
reader = opts[:reader] || SchemaTools::Reader
|
35
35
|
schema_location = opts[:path] || opts[:schema]
|
36
36
|
# remember schema + name on class level
|
37
|
-
|
37
|
+
|
38
|
+
self.schema= reader.read(schema_name, schema_location)
|
38
39
|
self.schema_name(schema_name)
|
39
40
|
# make getter / setter
|
40
41
|
self.schema[:properties].each do |key, prop|
|
@@ -45,8 +46,10 @@ module SchemaTools
|
|
45
46
|
end
|
46
47
|
|
47
48
|
# @param [Hash] schema_hash
|
48
|
-
def schema
|
49
|
-
@schema = schema_hash
|
49
|
+
def schema= schema_hash
|
50
|
+
@schema = schema_hash
|
51
|
+
end
|
52
|
+
def schema
|
50
53
|
@schema
|
51
54
|
end
|
52
55
|
|
@@ -34,8 +34,8 @@ module SchemaTools
|
|
34
34
|
# properties are used.
|
35
35
|
# @options opts [String] :path of the schema files overriding global one
|
36
36
|
# @options opts [String] :base_url used in all links
|
37
|
-
# @options opts [Boolean] :
|
38
|
-
#
|
37
|
+
# @options opts [Boolean] :links if set the object hash gets its _links
|
38
|
+
# array inline.
|
39
39
|
#
|
40
40
|
# @return [Hash{String=>{String=>Mixed}}] The object as hash:
|
41
41
|
# { 'invoice' => {'title'=>'hello world', 'number'=>'4711' } }
|
@@ -47,31 +47,30 @@ module SchemaTools
|
|
47
47
|
class_name = opts[:class_name] || real_class_name
|
48
48
|
|
49
49
|
# get schema
|
50
|
-
|
50
|
+
inline_schema = opts.delete(:schema) if opts[:schema].present?
|
51
|
+
schema = inline_schema || SchemaTools::Reader.read(class_name, opts[:path])
|
52
|
+
|
51
53
|
# iterate over the defined schema fields
|
52
54
|
data = parse_properties(obj, schema, opts)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if opts[:exclude_root]
|
57
|
-
hsh = data
|
58
|
-
hsh['_class_name'] = "#{class_name}"
|
59
|
-
links && hsh['_links'] = links
|
60
|
-
else
|
61
|
-
hsh = { "#{class_name}" => data }
|
62
|
-
links && hsh['links'] = links
|
55
|
+
if opts[:links]
|
56
|
+
links = parse_links(obj, schema, opts)
|
57
|
+
links && data['_links'] = links
|
63
58
|
end
|
64
|
-
|
59
|
+
data
|
65
60
|
end
|
66
61
|
|
67
62
|
private
|
68
63
|
|
64
|
+
# @param [Object] obj from which to grab the properties
|
65
|
+
# @param [Hash] schema
|
66
|
+
# @param [Hash] opts
|
69
67
|
def parse_properties(obj, schema, opts)
|
70
68
|
fields = opts[:fields]
|
71
69
|
data = {}
|
72
70
|
schema['properties'].each do |field, prop|
|
73
71
|
next if fields && !fields.include?(field)
|
74
72
|
if prop['type'] == 'array'
|
73
|
+
# ensure the nested object gets its own class name
|
75
74
|
opts.delete(:class_name)
|
76
75
|
data[field] = parse_list(obj, field, prop, opts)
|
77
76
|
elsif prop['type'] == 'object' # a singular related object
|
@@ -121,16 +120,29 @@ module SchemaTools
|
|
121
120
|
# @return [Array<Hash{String=>String}>]
|
122
121
|
def parse_list(obj, field, prop, opts)
|
123
122
|
res = []
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
123
|
+
# TODO should we raise errors if one of those is missing?
|
124
|
+
return nil if !obj.respond_to?( field )
|
125
|
+
return nil if !prop['items']
|
126
|
+
|
127
|
+
rel_objects = obj.send( field )
|
128
|
+
# force an empty array if values are not present
|
129
|
+
return res if !rel_objects
|
130
|
+
|
131
|
+
if prop['items'].is_a?(Hash) || prop['items'].is_a?(ActiveSupport::HashWithIndifferentAccess)
|
132
|
+
# array of plain values e.g number, strings e.g
|
133
|
+
# "items": { "type": "string" },
|
134
|
+
# should we convert the values? according to the type?
|
135
|
+
if SCHEMA_BASE_TYPES.include?(prop['items']['type'])
|
136
|
+
res = rel_objects
|
137
|
+
elsif prop['items']['type'] == 'object'
|
138
|
+
rel_objects.each { |rel_obj| res << from_schema(rel_obj, opts) }
|
132
139
|
end
|
133
140
|
end
|
141
|
+
|
142
|
+
if prop['items'].is_a?(Array)
|
143
|
+
#TODO recurse
|
144
|
+
# res << rel_objects.each { |rel_obj| parse_list(rel_obj, field, prop opts) }
|
145
|
+
end
|
134
146
|
res
|
135
147
|
end
|
136
148
|
|
@@ -141,24 +153,19 @@ module SchemaTools
|
|
141
153
|
# @param [Hash] opts to_schema options
|
142
154
|
# @return [Array<Hash{String=>String}>]
|
143
155
|
def parse_object(obj, field, prop, opts)
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
prop['properties'].each do |fld, prp|
|
158
|
-
res[fld] = rel_obj.send(fld) if rel_obj.respond_to?(fld)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
156
|
+
return if !obj.respond_to?( field )
|
157
|
+
rel_obj = obj.send( field )
|
158
|
+
return if !rel_obj
|
159
|
+
|
160
|
+
res = if prop['properties']
|
161
|
+
opts[:schema] = prop
|
162
|
+
from_schema(rel_obj, opts)
|
163
|
+
elsif prop['oneOf']
|
164
|
+
# auto-detects which schema to use depending on the rel_object type
|
165
|
+
# Simpler than detecting the object type or $ref to use inside the
|
166
|
+
# oneOf array
|
167
|
+
from_schema(rel_obj, opts)
|
168
|
+
end
|
162
169
|
res
|
163
170
|
end
|
164
171
|
|
@@ -35,10 +35,9 @@ module SchemaTools
|
|
35
35
|
def read(schema_name, path_or_schema=nil)
|
36
36
|
schema_name = schema_name.to_sym
|
37
37
|
return registry[schema_name] if registry[schema_name]
|
38
|
-
|
38
|
+
|
39
39
|
if path_or_schema.is_a?(::Hash)
|
40
|
-
|
41
|
-
plain_data = path_or_schema.to_json
|
40
|
+
schema = Schema.new(path_or_schema)
|
42
41
|
elsif path_or_schema.is_a?(::String) || path_or_schema.nil?
|
43
42
|
path = path_or_schema
|
44
43
|
file_path = File.join(path || SchemaTools.schema_path, "#{schema_name}.json")
|
@@ -48,25 +47,16 @@ module SchemaTools
|
|
48
47
|
# use only if we found something, else keep path which will throw error on file.open later
|
49
48
|
file_path = recursive_search || file_path
|
50
49
|
end
|
50
|
+
schema = Schema.new(file_path)
|
51
51
|
else
|
52
52
|
raise ArgumentError, 'Second parameter must be a path or a schema!'
|
53
53
|
end
|
54
54
|
|
55
|
-
plain_data ||= File.open(file_path, 'r'){|f| f.read}
|
56
55
|
|
57
|
-
schema = ActiveSupport::JSON.decode(plain_data).with_indifferent_access
|
58
56
|
# only import object definitions, shared property definitions are handled separate
|
59
57
|
return unless schema[:type] == 'object'
|
60
|
-
if schema[:extends]
|
61
|
-
extends = schema[:extends].is_a?(Array) ? schema[:extends] : [ schema[:extends] ]
|
62
|
-
extends.each do |ext_name|
|
63
|
-
ext = read(ext_name, path)
|
64
|
-
# current schema props win
|
65
|
-
schema[:properties] = ext[:properties].merge(schema[:properties])
|
66
|
-
end
|
67
|
-
end
|
68
|
-
_handle_reference_properties schema
|
69
58
|
registry[ schema_name ] = schema
|
59
|
+
return schema
|
70
60
|
end
|
71
61
|
|
72
62
|
# Read all available schemas from a given path(folder +subfolders) and
|
@@ -93,24 +83,6 @@ module SchemaTools
|
|
93
83
|
schemas
|
94
84
|
end
|
95
85
|
|
96
|
-
# Merge referenced property definitions into the given schema.
|
97
|
-
# e.g. each object has an updated_at field which we define in a single
|
98
|
-
# location(external file) instead of repeating the property def in each
|
99
|
-
# schema.
|
100
|
-
# @param [HashWithIndifferentAccess] schema - single schema
|
101
|
-
def _handle_reference_properties(schema)
|
102
|
-
return unless schema["properties"]
|
103
|
-
schema["properties"].each { |key, value|
|
104
|
-
next unless value["$ref"]
|
105
|
-
|
106
|
-
json_pointer = value["$ref"]
|
107
|
-
values_from_pointer = RefResolver.load_json_pointer json_pointer
|
108
|
-
schema["properties"][key].merge!(values_from_pointer) {|key, old, new|
|
109
|
-
old
|
110
|
-
}
|
111
|
-
schema["properties"][key].delete("$ref")
|
112
|
-
}
|
113
|
-
end
|
114
86
|
end
|
115
87
|
end
|
116
88
|
end
|
@@ -16,7 +16,7 @@ module SchemaTools
|
|
16
16
|
# @param [Hash] schema if the pointer refers to a local schema, this is this
|
17
17
|
# the hash to evaluate it against. If the pointer contains a uri to a
|
18
18
|
# referenced schema, an attempt is made to load
|
19
|
-
def self.load_json_pointer json_pointer,
|
19
|
+
def self.load_json_pointer json_pointer, relative_to = nil
|
20
20
|
# JSON Pointer is a big, abandoned WIP and we're going to
|
21
21
|
# start by only implementing the part's we need ...
|
22
22
|
if nil == (json_pointer =~ /^(.*)#(.*)/ )
|
@@ -25,14 +25,17 @@ module SchemaTools
|
|
25
25
|
|
26
26
|
uri = $1.strip
|
27
27
|
pointer = $2
|
28
|
-
|
28
|
+
schema = {}
|
29
29
|
if ! uri.empty?
|
30
30
|
uri = URI.parse(uri)
|
31
31
|
raise "must currently be a relative uri: #{json_pointer}" if uri.absolute?
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
# 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
|
37
|
+
end
|
38
|
+
open (path) {|f| schema = JSON.parse(f.read) }
|
36
39
|
end
|
37
40
|
|
38
41
|
return self._retrieve_pointer_from_object pointer, schema
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module SchemaTools
|
4
|
+
# Internal representation of a Schema. This is basically a wrapper around a
|
5
|
+
# HashWithIndifferentAccess ( for historical purposes ) as well as information
|
6
|
+
# concerning where the Schema was loaded from in order to resolve relative paths.
|
7
|
+
|
8
|
+
class Schema
|
9
|
+
#@param [String|Hash] name_or_hash Schema may be initialized with either a filename or a hash
|
10
|
+
def initialize(name_or_hash)
|
11
|
+
case name_or_hash
|
12
|
+
when (::Hash)
|
13
|
+
@hash = name_or_hash.with_indifferent_access
|
14
|
+
when (::String)
|
15
|
+
src = File.open(name_or_hash, 'r') { |f| f.read }
|
16
|
+
self.absolute_filename= name_or_hash
|
17
|
+
decode src
|
18
|
+
end
|
19
|
+
handle_extends
|
20
|
+
resolve_refs
|
21
|
+
end
|
22
|
+
|
23
|
+
##################################################################################
|
24
|
+
# Wrappers for internal Hash
|
25
|
+
##################################################################################
|
26
|
+
|
27
|
+
# @param [String|Symbol] key
|
28
|
+
def [](key)
|
29
|
+
@hash[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [String|Symbol] key
|
33
|
+
# @param [Mixed] value e.g String|Array|Hash
|
34
|
+
def []=(key, value)
|
35
|
+
@hash[key] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
def empty?
|
39
|
+
@hash.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def keys
|
43
|
+
@hash.keys
|
44
|
+
end
|
45
|
+
|
46
|
+
def ==(other)
|
47
|
+
case other
|
48
|
+
when (::Hash)
|
49
|
+
return other.with_indifferent_access == hash
|
50
|
+
when (ActiveSupport::HashWithIndifferentAccess)
|
51
|
+
return other == hash
|
52
|
+
when (Schema)
|
53
|
+
return other.hash == hash
|
54
|
+
else
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
##################################################################################
|
60
|
+
# /Wrappers for internal Hash
|
61
|
+
##################################################################################
|
62
|
+
|
63
|
+
|
64
|
+
# set the filename the Schema was loaded from
|
65
|
+
# @param [String] fn
|
66
|
+
def absolute_filename=(fn)
|
67
|
+
@absolute_filename = File.absolute_path(fn)
|
68
|
+
@absolute_dir = File.dirname (@absolute_filename)
|
69
|
+
end
|
70
|
+
|
71
|
+
# retrieve the filename the Schema was loaded from or nil if the Schema
|
72
|
+
# was constructed from a Hash
|
73
|
+
def absolute_filename
|
74
|
+
@absolute_filename || nil
|
75
|
+
end
|
76
|
+
|
77
|
+
# retrieve the base directory against which refs should be resolved.
|
78
|
+
def absolute_dir
|
79
|
+
@absolute_dir || SchemaTools.schema_path
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def hash
|
87
|
+
@hash
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# @param [String] src schema json string
|
93
|
+
def decode(src)
|
94
|
+
@hash = ActiveSupport::JSON.decode(src).with_indifferent_access
|
95
|
+
end
|
96
|
+
|
97
|
+
def handle_extends
|
98
|
+
if self[:extends]
|
99
|
+
extends = self[:extends].is_a?(Array) ? self[:extends] : [ self[:extends] ]
|
100
|
+
extends.each do |ext_name|
|
101
|
+
ext = Reader.read(ext_name, absolute_dir)
|
102
|
+
# current schema props win
|
103
|
+
self[:properties] = ext[:properties].merge(self[:properties] || {})
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Merge referenced property definitions into the given schema.
|
109
|
+
# e.g. each object has an updated_at field which we define in a single
|
110
|
+
# location(external file) instead of repeating the property def in each
|
111
|
+
# schema.
|
112
|
+
# any hash found along the way is processed recursively, we look for a
|
113
|
+
# "$ref" param and resolve it. Other params are checked for nested hashes
|
114
|
+
# and those are processed.
|
115
|
+
# @param [HashWithIndifferentAccess] schema - single schema
|
116
|
+
def resolve_refs schema = nil
|
117
|
+
schema ||= @hash
|
118
|
+
|
119
|
+
def resolve_reference hash
|
120
|
+
json_pointer = hash["$ref"]
|
121
|
+
values_from_pointer = RefResolver.load_json_pointer json_pointer, self
|
122
|
+
hash.merge!(values_from_pointer) { |key, old, new| old }
|
123
|
+
hash.delete("$ref")
|
124
|
+
end
|
125
|
+
|
126
|
+
keys = schema.keys # in case you are wondering: RuntimeError: can't add a new key into hash during iteration
|
127
|
+
keys.each do |k|
|
128
|
+
v = schema[k]
|
129
|
+
if k == "$ref"
|
130
|
+
resolve_reference schema
|
131
|
+
elsif v.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
132
|
+
resolve_refs v
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
data/lib/schema_tools/version.rb
CHANGED
data/spec/fixtures/client.json
CHANGED
@@ -63,7 +63,12 @@
|
|
63
63
|
"addresses":{
|
64
64
|
"description": "A client can have many addresses, sorted by date descending(new first). Default address is the most recent one.",
|
65
65
|
"type":"array",
|
66
|
-
"
|
66
|
+
"items" : {
|
67
|
+
"type" : "object",
|
68
|
+
"properties":{
|
69
|
+
"$ref":"./address.json#properties"
|
70
|
+
}
|
71
|
+
}
|
67
72
|
},
|
68
73
|
"work_address":{
|
69
74
|
"description": "The work address as an example for a single nested/related object",
|
data/spec/fixtures/contact.json
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"type" : "object",
|
3
|
+
"description" : "deeply nested",
|
4
|
+
"properties" : {
|
5
|
+
"contains_nesting" : {
|
6
|
+
"type" : "array",
|
7
|
+
"items" : {
|
8
|
+
"type" : "object",
|
9
|
+
"properties" : {
|
10
|
+
"own_property" : { "type" : "string" },
|
11
|
+
"nested_ref" : {
|
12
|
+
"type" : "object",
|
13
|
+
"properties" : {
|
14
|
+
"$ref" : "./basic_definitions.json#definitions/nested_object/properties"
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
data/spec/fixtures/lead.json
CHANGED
data/spec/fixtures/page.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"type": "object",
|
3
3
|
"title": "Page",
|
4
4
|
"name": "page",
|
5
|
-
"description": "A page of a PDF which can either belong to a PDF template or archived PDF.",
|
5
|
+
"description": "A page of a PDF which can either belong to a PDF template or archived PDF. Example taken from happyPDF",
|
6
6
|
"properties": {
|
7
7
|
"id": {
|
8
8
|
"description": "The object identifier.",
|
@@ -57,11 +57,15 @@
|
|
57
57
|
"type": "integer"
|
58
58
|
},
|
59
59
|
"blocks": {
|
60
|
-
"description": "Placeholder blocks on this page
|
60
|
+
"description": "Placeholder blocks on this page, if it belongs to a PDF Template",
|
61
61
|
"type": "array",
|
62
|
-
"
|
62
|
+
"items" : {
|
63
|
+
"type" : "object",
|
64
|
+
"properties":{
|
65
|
+
"$ref": "./basic_definitions.json#definitions/nested_object/properties"
|
66
|
+
}
|
67
|
+
}
|
63
68
|
}
|
64
|
-
|
65
69
|
},
|
66
70
|
"links": [
|
67
71
|
{
|
@@ -45,43 +45,33 @@ describe SchemaTools::Hash do
|
|
45
45
|
|
46
46
|
it 'should return hash' do
|
47
47
|
hash = SchemaTools::Hash.from_schema(contact)
|
48
|
-
hash['
|
48
|
+
hash['last_name'].should == 'Paul'
|
49
49
|
end
|
50
50
|
|
51
51
|
it 'should use custom schema path' do
|
52
52
|
custom_path = File.expand_path('../../fixtures', __FILE__)
|
53
53
|
hash = SchemaTools::Hash.from_schema(contact, path: custom_path)
|
54
|
-
hash['
|
54
|
+
hash['last_name'].should == 'Paul'
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'should use custom schema' do
|
58
58
|
hash = SchemaTools::Hash.from_schema(contact, class_name: :client)
|
59
|
-
hash['
|
59
|
+
hash['last_name'].should == 'Paul'
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'should use only give fields' do
|
63
63
|
hash = SchemaTools::Hash.from_schema(contact, fields: ['id', 'last_name'])
|
64
|
-
hash
|
65
|
-
hash['
|
66
|
-
hash['
|
67
|
-
hash['
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'should exclude root' do
|
71
|
-
hash = SchemaTools::Hash.from_schema(contact, exclude_root: true)
|
72
|
-
hash['last_name'].should == 'Paul'
|
73
|
-
hash['_class_name'].should == 'contact'
|
64
|
+
hash.keys.length.should == 2
|
65
|
+
hash['last_name'].should == contact.last_name
|
66
|
+
hash['id'].should == contact.id
|
67
|
+
hash['first_name'].should be_nil
|
74
68
|
end
|
75
69
|
|
76
70
|
it 'has _links on object if exclude root' do
|
77
|
-
hash = SchemaTools::Hash.from_schema(contact,
|
71
|
+
hash = SchemaTools::Hash.from_schema(contact, links: true, class_name: :client)
|
78
72
|
hash['_links'].length.should == 7
|
79
73
|
end
|
80
74
|
|
81
|
-
it 'has _class_name on object if exclude root' do
|
82
|
-
hash = SchemaTools::Hash.from_schema(contact, exclude_root: true, class_name: :client)
|
83
|
-
hash['_class_name'].should == 'client'
|
84
|
-
end
|
85
75
|
end
|
86
76
|
|
87
77
|
context 'with nested values referencing a schema' do
|
@@ -90,12 +80,12 @@ describe SchemaTools::Hash do
|
|
90
80
|
|
91
81
|
it 'has an empty array if values are missing' do
|
92
82
|
hash = SchemaTools::Hash.from_schema(client)
|
93
|
-
hash['
|
83
|
+
hash['addresses'].should == []
|
94
84
|
end
|
95
85
|
|
96
86
|
it 'has nil if nested object is missing' do
|
97
87
|
hash = SchemaTools::Hash.from_schema(client)
|
98
|
-
hash['
|
88
|
+
hash['work_address'].should be_nil
|
99
89
|
end
|
100
90
|
|
101
91
|
it 'has nested array values' do
|
@@ -104,7 +94,7 @@ describe SchemaTools::Hash do
|
|
104
94
|
a1.zip = 50733
|
105
95
|
client.addresses = [a1]
|
106
96
|
hash = SchemaTools::Hash.from_schema(client)
|
107
|
-
hash['
|
97
|
+
hash['addresses'].should == [{"city"=>"Cologne", "zip"=>50733}]
|
108
98
|
end
|
109
99
|
|
110
100
|
it 'has nested array values without root' do
|
@@ -113,7 +103,7 @@ describe SchemaTools::Hash do
|
|
113
103
|
a1.zip = 50733
|
114
104
|
client.addresses = [a1]
|
115
105
|
hash = SchemaTools::Hash.from_schema(client, exclude_root: true)
|
116
|
-
hash['addresses'].should == [{"city"=>"Cologne", "zip"=>50733
|
106
|
+
hash['addresses'].should == [{"city"=>"Cologne", "zip"=>50733}]
|
117
107
|
end
|
118
108
|
|
119
109
|
it 'has nested object value' do
|
@@ -122,16 +112,7 @@ describe SchemaTools::Hash do
|
|
122
112
|
a1.zip = 50733
|
123
113
|
client.work_address = a1
|
124
114
|
hash = SchemaTools::Hash.from_schema(client)
|
125
|
-
hash['
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'has nested object value without root' do
|
129
|
-
a1 = Address.new
|
130
|
-
a1.city = 'Cologne'
|
131
|
-
a1.zip = 50733
|
132
|
-
client.work_address = a1
|
133
|
-
hash = SchemaTools::Hash.from_schema(client, exclude_root: true)
|
134
|
-
hash['work_address'].should == {"city"=>"Cologne", "zip"=>50733, "_class_name"=>"address"}
|
115
|
+
hash['work_address'].should == {"city"=>"Cologne", "zip"=>50733}
|
135
116
|
end
|
136
117
|
|
137
118
|
it 'has nested oneOf type object ' do
|
@@ -143,7 +124,6 @@ describe SchemaTools::Hash do
|
|
143
124
|
|
144
125
|
hash = SchemaTools::Hash.from_schema(i, exclude_root: true)
|
145
126
|
hash['person']['first_name'].should == 'Pit'
|
146
|
-
hash['person']['_class_name'].should == 'contact'
|
147
127
|
end
|
148
128
|
|
149
129
|
end
|
@@ -164,12 +144,12 @@ describe SchemaTools::Hash do
|
|
164
144
|
end
|
165
145
|
|
166
146
|
it 'should create array with values' do
|
167
|
-
@hash['
|
147
|
+
@hash['links_clicked'].should == lead.links_clicked
|
168
148
|
end
|
169
149
|
|
170
150
|
it 'should create object with values' do
|
171
|
-
@hash['
|
172
|
-
@hash['
|
151
|
+
@hash['conversion']['from'].should == lead.conversion.from
|
152
|
+
@hash['conversion']['to'].should == lead.conversion.to
|
173
153
|
end
|
174
154
|
|
175
155
|
end
|
@@ -185,19 +165,19 @@ describe SchemaTools::Hash do
|
|
185
165
|
end
|
186
166
|
|
187
167
|
it 'has links' do
|
188
|
-
hash = SchemaTools::Hash.from_schema(client)
|
189
|
-
hash['
|
168
|
+
hash = SchemaTools::Hash.from_schema(client, links: true)
|
169
|
+
hash['_links'].length.should == 7
|
190
170
|
end
|
191
171
|
|
192
172
|
it 'should prepend base_url' do
|
193
|
-
hash = SchemaTools::Hash.from_schema(client, base_url: 'http://json-hell.com')
|
194
|
-
hash['
|
173
|
+
hash = SchemaTools::Hash.from_schema(client, base_url: 'http://json-hell.com', links: true)
|
174
|
+
hash['_links'].first['href'].should include( 'http://json-hell.com')
|
195
175
|
end
|
196
176
|
|
197
177
|
it 'should replace placeholders' do
|
198
178
|
client.id = 123
|
199
|
-
hash = SchemaTools::Hash.from_schema(client, base_url: 'http://json-hell.com')
|
200
|
-
hash['
|
179
|
+
hash = SchemaTools::Hash.from_schema(client, base_url: 'http://json-hell.com', links: true)
|
180
|
+
hash['_links'].last['href'].should == 'http://json-hell.com/clients/123/Peter'
|
201
181
|
end
|
202
182
|
|
203
183
|
end
|
@@ -3,9 +3,11 @@ require 'spec_helper'
|
|
3
3
|
class ClassWithSchemaAttrs
|
4
4
|
include SchemaTools::Modules::Attributes
|
5
5
|
has_schema_attrs :client
|
6
|
+
# test override of schema name
|
7
|
+
attr_accessor :contact_source
|
6
8
|
end
|
7
9
|
|
8
|
-
class
|
10
|
+
class ClassWithSchemaNameLead
|
9
11
|
include SchemaTools::Modules::AsSchema
|
10
12
|
schema_name :lead
|
11
13
|
end
|
@@ -14,6 +16,7 @@ end
|
|
14
16
|
module Test
|
15
17
|
class Address
|
16
18
|
include SchemaTools::Modules::AsSchema
|
19
|
+
attr_accessor :id, :city
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
@@ -34,14 +37,14 @@ describe SchemaTools::Modules::AsSchema do
|
|
34
37
|
it 'should return hash' do
|
35
38
|
subject.last_name = 'Hogan'
|
36
39
|
hsh = subject.as_schema_hash
|
37
|
-
hsh['
|
40
|
+
hsh['last_name'].should == 'Hogan'
|
38
41
|
end
|
39
42
|
|
40
43
|
it 'should return json' do
|
41
44
|
subject.last_name = 'Hogan'
|
42
45
|
json_str = subject.as_schema_json
|
43
46
|
hsh = ActiveSupport::JSON.decode(json_str)
|
44
|
-
hsh['
|
47
|
+
hsh['last_name'].should == 'Hogan'
|
45
48
|
end
|
46
49
|
|
47
50
|
end
|
@@ -49,15 +52,15 @@ describe SchemaTools::Modules::AsSchema do
|
|
49
52
|
describe 'schema name detection' do
|
50
53
|
|
51
54
|
it 'should use name from has_schema_attrs' do
|
52
|
-
ClassWithSchemaAttrs.new.as_schema_hash.keys.should include('
|
55
|
+
ClassWithSchemaAttrs.new.as_schema_hash.keys.should include('phone_mobile', 'cash_discount')
|
53
56
|
end
|
54
57
|
|
55
58
|
it 'should use schema_name defined in class' do
|
56
|
-
|
59
|
+
ClassWithSchemaNameLead.new.as_schema_hash.keys.should include('links_clicked')
|
57
60
|
end
|
58
61
|
|
59
|
-
it 'should
|
60
|
-
Test::Address.new.as_schema_hash.keys.should include('
|
62
|
+
it 'should infer schema from class name' do
|
63
|
+
Test::Address.new.as_schema_hash.keys.should include('city')
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
@@ -65,19 +68,11 @@ describe SchemaTools::Modules::AsSchema do
|
|
65
68
|
subject { ClassWithSchemaAttrs.new }
|
66
69
|
|
67
70
|
it 'should override schema name from' do
|
68
|
-
subject.as_schema_hash(class_name:'contact').keys.should include('
|
71
|
+
subject.as_schema_hash(class_name:'contact').keys.should include('contact_source')
|
69
72
|
end
|
70
73
|
|
71
74
|
it 'should use fields' do
|
72
|
-
subject.as_schema_hash(fields:['id'])
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'should exclude root' do
|
76
|
-
subject.as_schema_hash(exclude_root: true).keys.should include('id')
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'should use class name ' do
|
80
|
-
Test::Address.new.as_schema_hash.keys.should include('address')
|
75
|
+
subject.as_schema_hash(fields:['id']).keys.should == ['id']
|
81
76
|
end
|
82
77
|
end
|
83
78
|
|
@@ -56,6 +56,11 @@ describe SchemaTools::Reader do
|
|
56
56
|
schema[:properties][:id]["$ref"].should be_nil
|
57
57
|
end
|
58
58
|
|
59
|
+
it 'deals with nested referenced parameters properly' do
|
60
|
+
schema = SchemaTools::Reader.read(:includes_deep_nested_refs)
|
61
|
+
schema[:properties].should_not be_empty
|
62
|
+
end
|
63
|
+
|
59
64
|
it 'enforces correct parameter usage' do
|
60
65
|
expect { SchemaTools::Reader.read(:contact, []) }.to raise_error ArgumentError
|
61
66
|
end
|
@@ -66,6 +66,12 @@ describe SchemaTools::RefResolver do
|
|
66
66
|
it 'should load local ref' do
|
67
67
|
pointer = "./basic_definitions.json#definitions"
|
68
68
|
obj = SchemaTools::RefResolver.load_json_pointer(pointer)
|
69
|
-
obj.length.should eq
|
69
|
+
obj.length.should eq 3
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should load the entire document' do
|
73
|
+
pointer = "./basic_definitions.json#"
|
74
|
+
obj = SchemaTools::RefResolver.load_json_pointer(pointer)
|
75
|
+
obj["definitions"].keys.length.should eq 3
|
70
76
|
end
|
71
77
|
end
|
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.
|
4
|
+
version: 0.3.0
|
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-09
|
11
|
+
date: 2014-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -94,16 +94,19 @@ files:
|
|
94
94
|
- lib/schema_tools/modules/validations.rb
|
95
95
|
- lib/schema_tools/reader.rb
|
96
96
|
- lib/schema_tools/ref_resolver.rb
|
97
|
+
- lib/schema_tools/schema.rb
|
97
98
|
- lib/schema_tools/version.rb
|
98
99
|
- spec/fixtures/address.json
|
99
100
|
- spec/fixtures/basic_definitions.json
|
100
101
|
- spec/fixtures/client.json
|
101
102
|
- spec/fixtures/contact.json
|
102
103
|
- spec/fixtures/includes_basic_definitions.json
|
104
|
+
- spec/fixtures/includes_deep_nested_refs.json
|
103
105
|
- spec/fixtures/lead.json
|
104
106
|
- spec/fixtures/nested_schemas/person.json
|
105
107
|
- spec/fixtures/one_of_definition.json
|
106
108
|
- spec/fixtures/page.json
|
109
|
+
- spec/fixtures/relative_paths/includes_relative_path.json
|
107
110
|
- spec/schema_tools/cleaner_spec.rb
|
108
111
|
- spec/schema_tools/hash_spec.rb
|
109
112
|
- spec/schema_tools/klass_factory_spec.rb
|