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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDAyZTViOWEwMjk1YzMwNDQwZGYzYjBlODhiZDI5NzQ5OGRkOGY4YQ==
4
+ MmNjZDJjNDRjZTMyNjFjM2U0ZjFhMTRkMTJhZGNkNWVjYmVhZmU2MQ==
5
5
  data.tar.gz: !binary |-
6
- MTAyYmE1MDAwYTFlZGU2MjcwZDkxZDczODQxZjU3YzM3MWIzMGM3Mg==
6
+ NzI1OWY0OTU1ZDJmMDVmOTMyMTgzZTQ5ZjE3YjRmNDFjMzJmYzBjYg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NTRlNTI2OGVkYWExZTBkODRiNDVlZDNjMjdmMGFkNTM2MDQ3NTljYjNiM2Uw
10
- ZDFhNjc2Yzk3OWY4NjIzODg1ZTMwOWU2ZTlkNzZiYWRlMDg2NjAyZmM0YjE2
11
- NzZhOTYwNGRhZjI2NWI0MWFiNTkyNTA2ZjE1ODUzMTBjZDVhODI=
9
+ OTFmODcwMmM2MDBmZmU0YWZlYjIyODMyNjlkM2EyZWY1NTliZWI1YjQwYTU0
10
+ NGNlYTI1MmE1Y2FlMWY5ZGIwOGQwNzI2OTc5OTNlYTliMDIxMTZkZGQ2ODlj
11
+ N2NiNjFlNWQ0ZGVmM2E3M2M1NTZiM2U1NzM2ZjNmMmE2MGQ1MjM=
12
12
  data.tar.gz: !binary |-
13
- ZGUzOTcxZjJiMTE0MjEwOWJjMTgzNDRlZDNkNWQyYjYxODE0OWQwYWVlNTRm
14
- MDIzOGIyOGNkZjQwOGYwM2QxNWQ2ZjkxYjE1Y2MzZGNhMzk3MDBmOWRlYmQ5
15
- ODU0MzgzYmE5ZTEwYjRmNTI1YmY0NzlkZWZjY2FmYzNlZTg0ZTA=
13
+ MmMzNGU5NjkxYWQ1NzFkYzkzODcxNzI2NzY3ZTAwODE0OGY1YWUyOTY3ZTE1
14
+ ZGNjMGFhNGQ5YjU0YzNhZDJjYWIxNWUwN2M2NTNjMDZiMDI4ZmUwMTI0Njc1
15
+ ZDcyMzkzOTQ5Y2MzNzUwOTViODNkYjE5ZDIzNjY1NTJhNmFjMmI=
data/.gitignore CHANGED
@@ -8,3 +8,9 @@ Gemfile.lock
8
8
  doc/
9
9
  tmp/
10
10
  *.swp
11
+ .idea
12
+ .ruby-gemset
13
+ .ruby-version
14
+ *.swp
15
+ *.swo
16
+ tags
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
- self.schema( reader.read(schema_name, schema_location) )
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(schema_hash=nil)
49
- @schema = schema_hash if 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] :exclude_root if set objects are not nested under
38
- # their class name and the object hash gets _links and _class_name inline.
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
- schema = SchemaTools::Reader.read(class_name, opts[:path])
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
- #get links if present
54
- links = parse_links(obj, schema, opts)
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
- hsh
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
- if obj.respond_to?( field ) && rel_objects = obj.send( field )
125
- rel_objects.each do |rel_obj|
126
- res << if prop['properties'] && prop['properties']['$ref']
127
- #got schema describing the objects
128
- from_schema(rel_obj, opts)
129
- else
130
- rel_obj
131
- end
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
- res = nil
145
- if obj.respond_to?( field ) && rel_obj = obj.send( field )
146
- if prop['properties'] && prop['properties']['$ref']
147
- res = from_schema(rel_obj, opts)
148
- elsif prop['oneOf']
149
- # auto-detects which schema to use depending on the rel_object type
150
- # Simpler than detecting the object type or $ref to use inside the
151
- # oneOf array
152
- res = from_schema(rel_obj, opts)
153
- else
154
- # NO recursion directly get values from related object. Does
155
- # NOT allow deeper nesting so you MUST define an own schema to be save
156
- res = { }
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
- path = nil
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, schema = {}
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
- path = SchemaTools.schema_path + "/" + uri.path
33
- open (path) {|f|
34
- schema = JSON.parse(f.read)
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
+
@@ -1,3 +1,3 @@
1
1
  module SchemaTools
2
- VERSION = '0.2.6'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -8,6 +8,13 @@
8
8
  "some_other_property": {
9
9
  "description": "whatever",
10
10
  "type": "string"
11
+ },
12
+ "nested_object" : {
13
+ "type": "object",
14
+ "properties" : {
15
+ "prop1" : { "type" : "string"},
16
+ "prop2" : { "type" : "string"}
17
+ }
11
18
  }
12
19
  }
13
20
  }
@@ -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
- "properties" : {"$ref":"./address.json#properties"}
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",
@@ -9,6 +9,10 @@
9
9
  "readonly":true,
10
10
  "type":"number"
11
11
  },
12
+ "contact_source":{
13
+ "description": "Where did the contact came from.",
14
+ "type":"string"
15
+ },
12
16
  "organisation":{
13
17
  "description": "Name of a company. ",
14
18
  "required" : true,
@@ -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
+ }
@@ -12,7 +12,7 @@
12
12
  "links_clicked":{
13
13
  "description": "Timestamps of clicks. Test nested array values without object reference",
14
14
  "type":"array",
15
- "properties":{
15
+ "items":{
16
16
  "type": "string"
17
17
  }
18
18
  },
@@ -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,, if it belongs to a PDF Template",
60
+ "description": "Placeholder blocks on this page, if it belongs to a PDF Template",
61
61
  "type": "array",
62
- "properties": {"$ref":"./block.json#properties"}
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
  {
@@ -0,0 +1,6 @@
1
+ {
2
+ "type" : "object",
3
+ "properties" : {
4
+ "$ref" : "../basic_definitions.json#definitions/nested_object/properties"
5
+ }
6
+ }
@@ -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['contact']['last_name'].should == 'Paul'
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['contact']['last_name'].should == 'Paul'
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['client']['last_name'].should == 'Paul'
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['contact'].keys.length.should == 2
65
- hash['contact']['last_name'].should == contact.last_name
66
- hash['contact']['id'].should == contact.id
67
- hash['contact']['first_name'].should be_nil
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, exclude_root: true, class_name: :client)
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['client']['addresses'].should == []
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['client']['work_address'].should be_nil
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['client']['addresses'].should == [{"address"=>{"city"=>"Cologne", "zip"=>50733}}]
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, "_class_name"=>"address"}]
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['client']['work_address'].should == {"address"=>{"city"=>"Cologne", "zip"=>50733}}
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['lead']['links_clicked'].should == lead.links_clicked
147
+ @hash['links_clicked'].should == lead.links_clicked
168
148
  end
169
149
 
170
150
  it 'should create object with values' do
171
- @hash['lead']['conversion']['from'].should == lead.conversion.from
172
- @hash['lead']['conversion']['to'].should == lead.conversion.to
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['links'].length.should == 7
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['links'].first['href'].should include( 'http://json-hell.com')
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['links'].last['href'].should == 'http://json-hell.com/clients/123/Peter'
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 ClassWithSchemaName
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['client']['last_name'].should == 'Hogan'
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['client']['last_name'].should == 'Hogan'
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('client', 'links')
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
- ClassWithSchemaName.new.as_schema_hash.keys.should include('lead')
59
+ ClassWithSchemaNameLead.new.as_schema_hash.keys.should include('links_clicked')
57
60
  end
58
61
 
59
- it 'should use class name ' do
60
- Test::Address.new.as_schema_hash.keys.should include('address')
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('contact')
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'])['client'].keys.should == ['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 2
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.2.6
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 00:00:00.000000000 Z
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