json_schema_tools 0.2.6 → 0.3.0

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
- 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