jsi 0.0.4 → 0.4.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +3 -1
  3. data/CHANGELOG.md +48 -0
  4. data/LICENSE.md +613 -0
  5. data/README.md +84 -45
  6. data/jsi.gemspec +11 -14
  7. data/lib/jsi.rb +31 -12
  8. data/lib/jsi/base.rb +310 -344
  9. data/lib/jsi/base/to_rb.rb +2 -0
  10. data/lib/jsi/jsi_coder.rb +91 -0
  11. data/lib/jsi/json-schema-fragments.rb +3 -135
  12. data/lib/jsi/json.rb +3 -0
  13. data/lib/jsi/json/node.rb +72 -197
  14. data/lib/jsi/json/pointer.rb +419 -0
  15. data/lib/jsi/metaschema.rb +7 -0
  16. data/lib/jsi/metaschema_node.rb +218 -0
  17. data/lib/jsi/pathed_node.rb +118 -0
  18. data/lib/jsi/schema.rb +168 -223
  19. data/lib/jsi/schema_classes.rb +158 -0
  20. data/lib/jsi/simple_wrap.rb +12 -0
  21. data/lib/jsi/typelike_modules.rb +71 -45
  22. data/lib/jsi/util.rb +47 -57
  23. data/lib/jsi/version.rb +1 -1
  24. data/lib/schemas/json-schema.org/draft-04/schema.rb +7 -0
  25. data/lib/schemas/json-schema.org/draft-06/schema.rb +7 -0
  26. data/resources/icons/AGPL-3.0.png +0 -0
  27. data/test/base_array_test.rb +210 -84
  28. data/test/base_hash_test.rb +201 -58
  29. data/test/base_test.rb +212 -121
  30. data/test/jsi_coder_test.rb +85 -0
  31. data/test/jsi_json_arraynode_test.rb +26 -25
  32. data/test/jsi_json_hashnode_test.rb +40 -39
  33. data/test/jsi_json_node_test.rb +95 -126
  34. data/test/jsi_json_pointer_test.rb +102 -0
  35. data/test/jsi_typelike_as_json_test.rb +53 -0
  36. data/test/metaschema_node_test.rb +19 -0
  37. data/test/schema_module_test.rb +21 -0
  38. data/test/schema_test.rb +109 -97
  39. data/test/spreedly_openapi_test.rb +8 -0
  40. data/test/test_helper.rb +42 -8
  41. data/test/util_test.rb +14 -14
  42. metadata +54 -25
  43. data/LICENSE.txt +0 -21
  44. data/lib/jsi/schema_instance_json_coder.rb +0 -83
  45. data/lib/jsi/struct_json_coder.rb +0 -30
  46. data/test/schema_instance_json_coder_test.rb +0 -121
  47. data/test/struct_json_coder_test.rb +0 -130
@@ -0,0 +1,85 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe JSI::JSICoder do
4
+ let(:schema_content) do
5
+ {properties: {foo: {}, bar: {}}}
6
+ end
7
+ let(:schema) { JSI::Schema.new(schema_content) }
8
+ let(:options) { {} }
9
+ let(:schema_instance_json_coder) { JSI::JSICoder.new(schema, options) }
10
+ describe 'json' do
11
+ describe 'load' do
12
+ it 'loads nil' do
13
+ assert_nil(schema_instance_json_coder.load(nil))
14
+ end
15
+ it 'loads a hash' do
16
+ assert_equal(schema.new_jsi('foo' => 'bar'), schema_instance_json_coder.load({"foo" => "bar"}))
17
+ end
18
+ it 'loads something else' do
19
+ assert_equal(schema.new_jsi([[]]), schema_instance_json_coder.load([[]]))
20
+ end
21
+ describe 'array' do
22
+ let(:options) { {array: true} }
23
+ it 'loads an array of hashes' do
24
+ data = [{"foo" => "bar"}, {"foo" => "baz"}]
25
+ assert_equal([schema.new_jsi('foo' => 'bar'), schema.new_jsi('foo' => 'baz')], schema_instance_json_coder.load(data))
26
+ end
27
+ it 'loads an empty array' do
28
+ assert_equal([], schema_instance_json_coder.load([]))
29
+ end
30
+ it 'loads a not an array' do
31
+ assert_raises(TypeError) do
32
+ schema_instance_json_coder.load(Object.new)
33
+ end
34
+ end
35
+ end
36
+ describe 'array schema' do
37
+ let(:schema_content) { {items: {properties: {foo: {}, bar: {}}}} }
38
+ it 'loads an array of hashes' do
39
+ data = [{"foo" => "bar"}, {"foo" => "baz"}]
40
+ assert_equal(schema.new_jsi([{'foo' => 'bar'}, {'foo' => 'baz'}]), schema_instance_json_coder.load(data))
41
+ end
42
+ it 'loads an empty array' do
43
+ assert_equal(schema.new_jsi([]), schema_instance_json_coder.load([]))
44
+ end
45
+ it 'loads a not an array' do
46
+ instance = Object.new
47
+ assert_equal(schema.new_jsi(instance), schema_instance_json_coder.load(instance))
48
+ end
49
+ end
50
+ end
51
+ describe 'dump' do
52
+ it 'dumps nil' do
53
+ assert_nil(schema_instance_json_coder.dump(nil))
54
+ end
55
+ it 'dumps a schema_instance_class' do
56
+ assert_equal({"foo" => "x", "bar" => "y"}, schema_instance_json_coder.dump(schema.new_jsi(foo: 'x', bar: 'y')))
57
+ end
58
+ it 'dumps something else' do
59
+ assert_raises(TypeError) do
60
+ schema_instance_json_coder.dump(Object.new)
61
+ end
62
+ end
63
+ it 'dumps some of the keys of a schema_instance_class after loading in a partial one' do
64
+ schema_instance_class = schema_instance_json_coder.load({'foo' => 'who'})
65
+ assert_equal({'foo' => 'who'}, schema_instance_json_coder.dump(schema_instance_class))
66
+ schema_instance_class.bar = 'whar'
67
+ assert_equal({'foo' => 'who', 'bar' => 'whar'}, schema_instance_json_coder.dump(schema_instance_class))
68
+ end
69
+ describe 'array' do
70
+ let(:options) { {array: true} }
71
+ it 'dumps an array of schema_instances' do
72
+ schema_instances = [schema.new_jsi(foo: 'x', bar: 'y'), schema.new_jsi(foo: 'z', bar: 'q')]
73
+ assert_equal([{"foo" => "x", "bar" => "y"}, {"foo" => "z", "bar" => "q"}], schema_instance_json_coder.dump(schema_instances))
74
+ end
75
+ end
76
+ describe 'array schema' do
77
+ let(:schema_content) { {items: {properties: {foo: {}, bar: {}}}} }
78
+ it 'dumps a schema_instance array' do
79
+ schema_instances = schema.new_jsi([{foo: 'x', bar: 'y'}, {foo: 'z', bar: 'q'}])
80
+ assert_equal([{"foo" => "x", "bar" => "y"}, {"foo" => "z", "bar" => "q"}], schema_instance_json_coder.dump(schema_instances))
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -3,23 +3,24 @@ require_relative 'test_helper'
3
3
  document_types = [
4
4
  {
5
5
  make_document: -> (d) { d },
6
- document: ['a', ['b', 'q'], {'c' => {'d' => 'e'}}],
6
+ node_document: ['a', ['b', 'q'], {'c' => {'d' => 'e'}}],
7
7
  type_desc: 'Array',
8
8
  },
9
9
  {
10
10
  make_document: -> (d) { SortOfArray.new(d) },
11
- document: SortOfArray.new(['a', SortOfArray.new(['b', 'q']), SortOfHash.new({'c' => SortOfHash.new({'d' => 'e'})})]),
11
+ node_document: SortOfArray.new(['a', SortOfArray.new(['b', 'q']), SortOfHash.new({'c' => SortOfHash.new({'d' => 'e'})})]),
12
12
  type_desc: 'sort of Array-like',
13
13
  },
14
14
  ]
15
15
  document_types.each do |document_type|
16
16
  describe "JSI::JSON::ArrayNode with #{document_type[:type_desc]}" do
17
- # document of the node being tested
18
- let(:document) { document_type[:document] }
17
+ # node_document of the node being tested
18
+ let(:node_document) { document_type[:node_document] }
19
19
  # by default the node is the whole document
20
20
  let(:path) { [] }
21
+ let(:node_ptr) { JSI::JSON::Pointer.new(path) }
21
22
  # the node being tested
22
- let(:node) { JSI::JSON::Node.new_by_type(document, path) }
23
+ let(:node) { JSI::JSON::Node.new_by_type(node_document, node_ptr) }
23
24
 
24
25
  describe '#[] bad index' do
25
26
  it 'improves TypeError for Array subsript' do
@@ -55,7 +56,7 @@ document_types.each do |document_type|
55
56
  end
56
57
  end
57
58
  describe '#as_json' do
58
- let(:document) { document_type[:make_document].call(['a', 'b']) }
59
+ let(:node_document) { document_type[:make_document].call(['a', 'b']) }
59
60
  it '#as_json' do
60
61
  assert_equal(['a', 'b'], node.as_json)
61
62
  assert_equal(['a', 'b'], node.as_json(some_option: false))
@@ -107,35 +108,35 @@ document_types.each do |document_type|
107
108
  # for this reason, rassoc is NOT defined on Arraylike. it's here with as_json.
108
109
  #
109
110
  # I've never even seen anybody use rassoc. of all the methods to put into the standard library ...
110
- it('#rassoc') { assert_equal(['b', 'q'], node.as_json.rassoc('q')) }
111
+ it('#rassoc') { assert_equal(['b', 'q'], node.as_json.rassoc('q')) }
111
112
  it('#repeated_combination') { assert_equal([[]], node.repeated_combination(0).to_a) }
112
113
  it('#repeated_permutation') { assert_equal([[]], node.repeated_permutation(0).to_a) }
113
- it('#reverse') { assert_equal([node[2], node[1], 'a'], node.reverse) }
114
- it('#reverse_each') { assert_equal([node[2], node[1], 'a'], node.reverse_each.to_a) }
115
- it('#rindex') { assert_equal(0, node.rindex('a')) }
116
- it('#rotate') { assert_equal([node[1], node[2], 'a'], node.rotate) }
117
- it('#sample') { assert_equal('a', JSI::JSON::Node.new_doc(['a']).sample) }
118
- it('#shelljoin') { assert_equal('a', JSI::JSON::Node.new_doc(['a']).shelljoin) } if [].respond_to?(:shelljoin)
119
- it('#shuffle') { assert_equal(3, node.shuffle.size) }
120
- it('#slice') { assert_equal(['a'], node.slice(0, 1)) }
121
- it('#sort') { assert_equal(['a'], JSI::JSON::Node.new_doc(['a']).sort) }
122
- it('#take') { assert_equal(['a'], node.take(1)) }
123
- it('#take_while') { assert_equal([], node.take_while { false }) }
124
- it('#transpose') { assert_equal([], JSI::JSON::Node.new_doc([]).transpose) }
125
- it('#uniq') { assert_equal(node.to_a, node.uniq) }
126
- it('#values_at') { assert_equal(['a'], node.values_at(0)) }
127
- it('#zip') { assert_equal([['a', 'a'], [node[1], node[1]], [node[2], node[2]]], node.zip(node)) }
114
+ it('#reverse') { assert_equal([node[2], node[1], 'a'], node.reverse) }
115
+ it('#reverse_each') { assert_equal([node[2], node[1], 'a'], node.reverse_each.to_a) }
116
+ it('#rindex') { assert_equal(0, node.rindex('a')) }
117
+ it('#rotate') { assert_equal([node[1], node[2], 'a'], node.rotate) }
118
+ it('#sample') { assert_equal('a', JSI::JSON::Node.new_doc(['a']).sample) }
119
+ it('#shelljoin') { assert_equal('a', JSI::JSON::Node.new_doc(['a']).shelljoin) } if [].respond_to?(:shelljoin)
120
+ it('#shuffle') { assert_equal(3, node.shuffle.size) }
121
+ it('#slice') { assert_equal(['a'], node.slice(0, 1)) }
122
+ it('#sort') { assert_equal(['a'], JSI::JSON::Node.new_doc(['a']).sort) }
123
+ it('#take') { assert_equal(['a'], node.take(1)) }
124
+ it('#take_while') { assert_equal([], node.take_while { false }) }
125
+ it('#transpose') { assert_equal([], JSI::JSON::Node.new_doc([]).transpose) }
126
+ it('#uniq') { assert_equal(node.to_a, node.uniq) }
127
+ it('#values_at') { assert_equal(['a'], node.values_at(0)) }
128
+ it('#zip') { assert_equal([['a', 'a'], [node[1], node[1]], [node[2], node[2]]], node.zip(node)) }
128
129
  end
129
130
  describe 'modified copy methods' do
130
131
  it('#reject') { assert_equal(JSI::JSON::Node.new_doc(['a']), node.reject { |e| e != 'a' }) }
131
132
  it('#select') { assert_equal(JSI::JSON::Node.new_doc(['a']), node.select { |e| e == 'a' }) }
132
- it('#compact') { assert_equal(JSI::JSON::Node.new_doc(node.content.to_ary), node.compact) }
133
+ it('#compact') { assert_equal(JSI::JSON::Node.new_doc(node.node_content.to_ary), node.compact) }
133
134
  describe 'at a depth' do
134
- let(:document) { document_type[:make_document].call([['b', 'q'], {'c' => ['d', 'e']}]) }
135
+ let(:node_document) { document_type[:make_document].call([['b', 'q'], {'c' => ['d', 'e']}]) }
135
136
  let(:path) { ['1', 'c'] }
136
137
  it('#select') do
137
138
  selected = node.select { |e| e == 'd' }
138
- equivalent = JSI::JSON::Node.new_by_type([['b', 'q'], {'c' => ['d']}], ['1', 'c'])
139
+ equivalent = JSI::JSON::Node.new_by_type([['b', 'q'], {'c' => ['d']}], node_ptr)
139
140
  assert_equal(equivalent, selected)
140
141
  end
141
142
  end
@@ -3,23 +3,24 @@ require_relative 'test_helper'
3
3
  document_types = [
4
4
  {
5
5
  make_document: -> (d) { d },
6
- document: {'a' => 'b', 'c' => {'d' => 'e'}},
6
+ node_document: {'a' => 'b', 'c' => {'d' => 'e'}},
7
7
  type_desc: 'Hash',
8
8
  },
9
9
  {
10
10
  make_document: -> (d) { SortOfHash.new(d) },
11
- document: SortOfHash.new({'a' => 'b', 'c' => SortOfHash.new({'d' => 'e'})}),
11
+ node_document: SortOfHash.new({'a' => 'b', 'c' => SortOfHash.new({'d' => 'e'})}),
12
12
  type_desc: 'sort of Hash-like',
13
13
  },
14
14
  ]
15
15
  document_types.each do |document_type|
16
16
  describe "JSI::JSON::HashNode with #{document_type[:type_desc]}" do
17
- # document of the node being tested
18
- let(:document) { document_type[:document] }
17
+ # node_document of the node being tested
18
+ let(:node_document) { document_type[:node_document] }
19
19
  # by default the node is the whole document
20
20
  let(:path) { [] }
21
+ let(:node_ptr) { JSI::JSON::Pointer.new(path) }
21
22
  # the node being tested
22
- let(:node) { JSI::JSON::Node.new_by_type(document, path) }
23
+ let(:node) { JSI::JSON::Node.new_by_type(node_document, node_ptr) }
23
24
 
24
25
  describe '#each' do
25
26
  it 'iterates, one argument' do
@@ -54,21 +55,21 @@ document_types.each do |document_type|
54
55
  end
55
56
  end
56
57
  describe '#merge' do
57
- let(:document) { document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}) }
58
+ let(:node_document) { document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}) }
58
59
  # testing the node at 'c' here, merging a hash at a path within a document.
59
60
  let(:path) { ['c'] }
60
61
  it 'merges' do
61
62
  merged = node.merge('x' => 'y')
62
- # check the content at 'c' was merged with the remainder of the document intact (at 'a')
63
- assert_equal({'a' => {'b' => 0}, 'c' => {'d' => 'e', 'x' => 'y'}}, merged.document)
63
+ # check the node_content at 'c' was merged with the remainder of the document intact (at 'a')
64
+ assert_equal({'a' => {'b' => 0}, 'c' => {'d' => 'e', 'x' => 'y'}}, merged.node_document)
64
65
  # check the original node retains its original document
65
- assert_equal(document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}), node.document)
66
+ assert_equal(document_type[:make_document].call({'a' => {'b' => 0}, 'c' => {'d' => 'e'}}), node.node_document)
66
67
  # check that unnecessary copies of unaffected parts of the document were not made
67
- assert_equal(node.document.to_hash['a'].object_id, merged.document['a'].object_id)
68
+ assert_equal(node.node_document.to_hash['a'].object_id, merged.node_document['a'].object_id)
68
69
  end
69
70
  end
70
71
  describe '#as_json' do
71
- let(:document) { document_type[:make_document].call({'a' => 'b'}) }
72
+ let(:node_document) { document_type[:make_document].call({'a' => 'b'}) }
72
73
  it '#as_json' do
73
74
  assert_equal({'a' => 'b'}, node.as_json)
74
75
  assert_equal({'a' => 'b'}, node.as_json(this_option: 'what?'))
@@ -80,46 +81,46 @@ document_types.each do |document_type|
80
81
  it('#empty?') { assert_equal(false, node.empty?) }
81
82
  it('#has_key?') { assert_equal(true, node.has_key?('a')) }
82
83
  it('#include?') { assert_equal(false, node.include?('q')) }
83
- it('#key?') { assert_equal(true, node.key?('c')) }
84
- it('#keys') { assert_equal(['a', 'c'], node.keys) }
85
- it('#length') { assert_equal(2, node.length) }
86
- it('#member?') { assert_equal(false, node.member?(0)) }
87
- it('#size') { assert_equal(2, node.size) }
84
+ it('#key?') { assert_equal(true, node.key?('c')) }
85
+ it('#keys') { assert_equal(['a', 'c'], node.keys) }
86
+ it('#length') { assert_equal(2, node.length) }
87
+ it('#member?') { assert_equal(false, node.member?(0)) }
88
+ it('#size') { assert_equal(2, node.size) }
88
89
  end
89
90
  describe 'key + value methods' do
90
- it('#<') { assert_equal(true, node < {'a' => 'b', 'c' => node['c'], 'x' => 'y'}) } if {}.respond_to?(:<)
91
- it('#<=') { assert_equal(true, node <= node) } if {}.respond_to?(:<=)
92
- it('#>') { assert_equal(true, node > {}) } if {}.respond_to?(:>)
93
- it('#>=') { assert_equal(false, node >= {'foo' => 'bar'}) } if {}.respond_to?(:>=)
94
- it('#any?') { assert_equal(false, node.any? { |k, v| v == 3 }) }
95
- it('#assoc') { assert_equal(['a', 'b'], node.assoc('a')) }
96
- it('#dig') { assert_equal('e', node.dig('c', 'd')) } if {}.respond_to?(:dig)
97
- it('#each_pair') { assert_equal([['a', 'b'], ['c', node['c']]], node.each_pair.to_a) }
98
- it('#each_value') { assert_equal(['b', node['c']], node.each_value.to_a) }
99
- it('#fetch') { assert_equal('b', node.fetch('a')) }
91
+ it('#<') { assert_equal(true, node < {'a' => 'b', 'c' => node['c'], 'x' => 'y'}) } if {}.respond_to?(:<)
92
+ it('#<=') { assert_equal(true, node <= node) } if {}.respond_to?(:<=)
93
+ it('#>') { assert_equal(true, node > {}) } if {}.respond_to?(:>)
94
+ it('#>=') { assert_equal(false, node >= {'foo' => 'bar'}) } if {}.respond_to?(:>=)
95
+ it('#any?') { assert_equal(false, node.any? { |k, v| v == 3 }) }
96
+ it('#assoc') { assert_equal(['a', 'b'], node.assoc('a')) }
97
+ it('#dig') { assert_equal('e', node.dig('c', 'd')) } if {}.respond_to?(:dig)
98
+ it('#each_pair') { assert_equal([['a', 'b'], ['c', node['c']]], node.each_pair.to_a) }
99
+ it('#each_value') { assert_equal(['b', node['c']], node.each_value.to_a) }
100
+ it('#fetch') { assert_equal('b', node.fetch('a')) }
100
101
  it('#fetch_values') { assert_equal(['b'], node.fetch_values('a')) } if {}.respond_to?(:fetch_values)
101
- it('#has_value?') { assert_equal(true, node.has_value?('b')) }
102
- it('#invert') { assert_equal({'b' => 'a', node['c'] => 'c'}, node.invert) }
103
- it('#key') { assert_equal('a', node.key('b')) }
104
- it('#rassoc') { assert_equal(['a', 'b'], node.rassoc('b')) }
105
- it('#to_h') { assert_equal({'a' => 'b', 'c' => node['c']}, node.to_h) }
106
- it('#to_proc') { assert_equal('b', node.to_proc.call('a')) } if {}.respond_to?(:to_proc)
102
+ it('#has_value?') { assert_equal(true, node.has_value?('b')) }
103
+ it('#invert') { assert_equal({'b' => 'a', node['c'] => 'c'}, node.invert) }
104
+ it('#key') { assert_equal('a', node.key('b')) }
105
+ it('#rassoc') { assert_equal(['a', 'b'], node.rassoc('b')) }
106
+ it('#to_h') { assert_equal({'a' => 'b', 'c' => node['c']}, node.to_h) }
107
+ it('#to_proc') { assert_equal('b', node.to_proc.call('a')) } if {}.respond_to?(:to_proc)
107
108
  if {}.respond_to?(:transform_values)
108
109
  it('#transform_values') { assert_equal({'a' => nil, 'c' => nil}, node.transform_values { |_| nil }) }
109
110
  end
110
- it('#value?') { assert_equal(false, node.value?('0')) }
111
- it('#values') { assert_equal(['b', node['c']], node.values) }
112
- it('#values_at') { assert_equal(['b'], node.values_at('a')) }
111
+ it('#value?') { assert_equal(false, node.value?('0')) }
112
+ it('#values') { assert_equal(['b', node['c']], node.values) }
113
+ it('#values_at') { assert_equal(['b'], node.values_at('a')) }
113
114
  end
114
115
  describe 'modified copy methods' do
115
116
  # I'm going to rely on the #merge test above to test the modified copy functionality and just do basic
116
117
  # tests of all the modified copy methods here
117
- it('#merge') { assert_equal(JSI::JSON::Node.new_doc(node.content.to_hash), node.merge({})) }
118
- it('#reject') { assert_equal(JSI::JSON::Node.new_doc({}), node.reject { true }) }
119
- it('#select') { assert_equal(JSI::JSON::Node.new_doc({}), node.select { false }) }
118
+ it('#merge') { assert_equal(JSI::JSON::Node.new_doc(node.node_content), node.merge({})) }
119
+ it('#reject') { assert_equal(JSI::JSON::Node.new_doc({}), node.reject { true }) }
120
+ it('#select') { assert_equal(JSI::JSON::Node.new_doc({}), node.select { false }) }
120
121
  # Hash#compact only available as of ruby 2.5.0
121
122
  if {}.respond_to?(:compact)
122
- it('#compact') { assert_equal(JSI::JSON::Node.new_doc({"a" => "b", "c" => node.content.to_hash["c"]}), node.compact) }
123
+ it('#compact') { assert_equal(JSI::JSON::Node.new_doc({"a" => "b", "c" => node.node_content.to_hash["c"]}), node.compact) }
123
124
  end
124
125
  end
125
126
  JSI::Hashlike::DESTRUCTIVE_METHODS.each do |destructive_method_name|
@@ -2,61 +2,70 @@ require_relative 'test_helper'
2
2
 
3
3
  describe JSI::JSON::Node do
4
4
  let(:path) { [] }
5
- let(:node) { JSI::JSON::Node.new(document, path) }
5
+ let(:node_ptr) { JSI::JSON::Pointer.new(path) }
6
+ let(:node) { JSI::JSON::Node.new(node_document, node_ptr) }
6
7
 
7
8
  describe 'initialization' do
8
9
  it 'initializes' do
9
- node = JSI::JSON::Node.new({'a' => 'b'}, [])
10
- assert_equal({'a' => 'b'}, node.document)
11
- assert_equal([], node.path)
10
+ node = JSI::JSON::Node.new({'a' => 'b'}, node_ptr)
11
+ assert_equal({'a' => 'b'}, node.node_document)
12
+ assert_equal(JSI::JSON::Pointer.new([]), node.node_ptr)
13
+ end
14
+ it 'initializes, node_ptr is not node_ptr' do
15
+ err = assert_raises(TypeError) { JSI::JSON::Node.new({'a' => 'b'}, []) }
16
+ assert_equal('node_ptr must be a JSI::JSON::Pointer. got: [] (Array)', err.message)
17
+ end
18
+ it 'initializes, node_document is another Node' do
19
+ err = assert_raises(TypeError) { JSI::JSON::Node.new(JSI::JSON::Node.new({'a' => 'b'}, node_ptr), node_ptr) }
20
+ assert_equal("node_document of a Node should not be another JSI::JSON::Node: #<JSI::JSON::Node # {\"a\"=>\"b\"}>", err.message)
12
21
  end
13
22
  end
14
23
  describe 'initialization by .new_by_type' do
15
24
  it 'initializes HashNode' do
16
25
  node = JSI::JSON::Node.new_doc({'a' => 'b'})
17
26
  assert_instance_of(JSI::JSON::HashNode, node)
18
- assert_equal({'a' => 'b'}, node.document)
27
+ assert_equal({'a' => 'b'}, node.node_document)
19
28
  end
20
29
  it 'initializes ArrayNode' do
21
30
  node = JSI::JSON::Node.new_doc(['a', 'b'])
22
31
  assert_instance_of(JSI::JSON::ArrayNode, node)
23
- assert_equal(['a', 'b'], node.document)
32
+ assert_equal(['a', 'b'], node.node_document)
24
33
  end
25
34
  it 'initializes Node' do
26
35
  object = Object.new
27
36
  node = JSI::JSON::Node.new_doc(object)
28
37
  assert_instance_of(JSI::JSON::Node, node)
29
- assert_equal(object, node.document)
38
+ assert_equal(object, node.node_document)
30
39
  end
31
40
  end
32
- describe '#pointer' do
33
- it 'is a ::JSON::Schema::Pointer' do
34
- assert_instance_of(::JSON::Schema::Pointer, JSI::JSON::Node.new({}, []).pointer)
41
+ describe '#node_ptr' do
42
+ it 'is a JSI::JSON::Pointer' do
43
+ assert_instance_of(JSI::JSON::Pointer, JSI::JSON::Node.new({}, node_ptr).node_ptr)
35
44
  end
36
45
  end
37
- describe '#content' do
38
- it 'returns the content at the root' do
39
- assert_equal({'a' => 'b'}, JSI::JSON::Node.new({'a' => 'b'}, []).content)
46
+ describe '#node_content' do
47
+ it 'returns the node_content at the root' do
48
+ assert_equal({'a' => 'b'}, JSI::JSON::Node.new({'a' => 'b'}, node_ptr).node_content)
40
49
  end
41
- it 'returns the content from the deep' do
42
- assert_equal('b', JSI::JSON::Node.new([0, {'x' => [{'a' => ['b']}]}], [1, 'x', 0, 'a', 0]).content)
50
+ it 'returns the node_content from the deep' do
51
+ assert_equal('b', JSI::JSON::Node.new([0, {'x' => [{'a' => ['b']}]}], JSI::JSON::Pointer.new([1, 'x', 0, 'a', 0])).node_content)
43
52
  end
44
53
  end
45
54
  describe '#deref' do
46
- let(:document) do
55
+ let(:node_document) do
47
56
  {
48
57
  'foo' => {'bar' => ['baz']},
49
58
  'a' => {'$ref' => '#/foo'},
50
59
  }
51
60
  end
52
61
  it 'follows a $ref' do
53
- assert_equal({'bar' => ['baz']}, node['a'].deref.content)
62
+ assert_equal({'bar' => ['baz']}, node['a'].deref.node_content)
54
63
  end
55
64
  it 'returns the node when there is no $ref to follow' do
56
- assert_equal({'bar' => ['baz']}, node['foo'].deref.content)
65
+ assert_equal({'bar' => ['baz']}, node['foo'].deref.node_content)
57
66
  end
58
67
  describe "dealing with google's invalid $refs" do
59
- let(:document) do
68
+ let(:node_document) do
60
69
  {
61
70
  'schemas' => {'bar' => {'description' => ['baz']}},
62
71
  'a' => {'$ref' => 'bar', 'foo' => 'bar'},
@@ -64,163 +73,122 @@ describe JSI::JSON::Node do
64
73
  end
65
74
  it 'subscripts a node consisting of a $ref WITHOUT following' do
66
75
  subscripted = node['a']
67
- assert_equal({'$ref' => 'bar', 'foo' => 'bar'}, subscripted.content)
68
- assert_equal(['a'], subscripted.path)
76
+ assert_equal({'$ref' => 'bar', 'foo' => 'bar'}, subscripted.node_content)
77
+ assert_equal(JSI::JSON::Pointer.new(['a']), subscripted.node_ptr)
69
78
  end
70
79
  it 'looks for a node in #/schemas with the name of the $ref' do
71
- assert_equal({'description' => ['baz']}, node['a'].deref.content)
72
- end
73
- it 'follows a $ref when subscripting past it' do
74
- subscripted = node['a']['description']
75
- assert_equal(['baz'], subscripted.content)
76
- assert_equal(['schemas', 'bar', 'description'], subscripted.path)
77
- end
78
- it 'does not follow a $ref when subscripting a key that is present' do
79
- subscripted = node['a']['foo']
80
- assert_equal('bar', subscripted)
80
+ assert_equal({'description' => ['baz']}, node['a'].deref.node_content)
81
81
  end
82
82
  end
83
83
  describe "dealing with whatever this is" do
84
84
  # I think google uses this style in some cases maybe. I don't remember.
85
- let(:document) do
85
+ let(:node_document) do
86
86
  {
87
87
  'schemas' => {'bar' => {'id' => 'BarID', 'description' => 'baz'}},
88
88
  'a' => {'$ref' => 'BarID'},
89
89
  }
90
90
  end
91
91
  it 'looks for a node in #/schemas with the name of the $ref' do
92
- assert_equal({'id' => 'BarID', 'description' => 'baz'}, node['a'].deref.content)
92
+ assert_equal({'id' => 'BarID', 'description' => 'baz'}, node['a'].deref.node_content)
93
93
  end
94
94
  end
95
95
  end
96
96
  describe '#[]' do
97
97
  describe 'without dereferencing' do
98
- let(:document) { [0, {'x' => [{'a' => ['b']}]}] }
98
+ let(:node_document) { [0, {'x' => [{'a' => ['b']}]}] }
99
99
  it 'subscripts arrays and hashes' do
100
100
  assert_equal('b', node[1]['x'][0]['a'][0])
101
101
  end
102
102
  it 'returns ArrayNode for an array' do
103
103
  subscripted = node[1]['x']
104
104
  assert_instance_of(JSI::JSON::ArrayNode, subscripted)
105
- assert_equal([{'a' => ['b']}], subscripted.content)
106
- assert_equal([1, 'x'], subscripted.path)
105
+ assert_equal([{'a' => ['b']}], subscripted.node_content)
106
+ assert_equal(JSI::JSON::Pointer.new([1, 'x']), subscripted.node_ptr)
107
107
  end
108
108
  it 'returns HashNode for a Hash' do
109
109
  subscripted = node[1]
110
110
  assert_instance_of(JSI::JSON::HashNode, subscripted)
111
- assert_equal({'x' => [{'a' => ['b']}]}, subscripted.content)
112
- assert_equal([1], subscripted.path)
111
+ assert_equal({'x' => [{'a' => ['b']}]}, subscripted.node_content)
112
+ assert_equal(JSI::JSON::Pointer.new([1]), subscripted.node_ptr)
113
+ end
114
+ describe 'node_content does not respond to []' do
115
+ let(:node_document) { Object.new }
116
+ it 'cannot subscript' do
117
+ err = assert_raises(NoMethodError) { node['x'] }
118
+ assert_equal("undefined method `[]`\nsubscripting with \"x\" (String) from Object. content is: #{node_document.pretty_inspect.chomp}", err.message)
119
+ end
113
120
  end
114
121
  end
115
122
  describe 'with dereferencing' do
116
- let(:document) do
123
+ let(:node_document) do
117
124
  {
118
125
  'foo' => {'bar' => ['baz']},
119
126
  'a' => {'$ref' => '#/foo', 'description' => 'hi'}, # not sure a description is actually allowed here, whatever
120
127
  }
121
128
  end
122
- it 'subscripts a node consisting of a $ref WITHOUT following' do
129
+ it 'subscripts a node consisting of a $ref without following' do
123
130
  subscripted = node['a']
124
- assert_equal({'$ref' => '#/foo', 'description' => 'hi'}, subscripted.content)
125
- assert_equal(['a'], subscripted.path)
126
- end
127
- it 'follows a $ref when subscripting past it' do
128
- subscripted = node['a']['bar']
129
- assert_equal(['baz'], subscripted.content)
130
- assert_equal(['foo', 'bar'], subscripted.path)
131
- end
132
- it 'does not follow a $ref when subscripting a key that is present' do
133
- subscripted = node['a']['description']
134
- assert_equal('hi', subscripted)
131
+ assert_equal({'$ref' => '#/foo', 'description' => 'hi'}, subscripted.node_content)
132
+ assert_equal(JSI::JSON::Pointer.new(['a']), subscripted.node_ptr)
135
133
  end
136
134
  end
137
135
  end
138
136
  describe '#[]=' do
139
- let(:document) { [0, {'x' => [{'a' => ['b']}]}] }
137
+ let(:node_document) { [0, {'x' => [{'a' => ['b']}]}] }
140
138
  it 'assigns' do
141
139
  node[0] = 'abcdefg'
142
- assert_equal(['abcdefg', {'x' => [{'a' => ['b']}]}], document)
143
- string_node = JSI::JSON::Node.new(document, [0])
140
+ assert_equal(['abcdefg', {'x' => [{'a' => ['b']}]}], node_document)
141
+ string_node = JSI::JSON::Node.new(node_document, JSI::JSON::Pointer.new([0]))
144
142
  string_node[0..2] = '0'
145
- assert_equal(['0defg', {'x' => [{'a' => ['b']}]}], document)
143
+ assert_equal(['0defg', {'x' => [{'a' => ['b']}]}], node_document)
146
144
  node[0] = node[1]
147
- assert_equal([{'x' => [{'a' => ['b']}]}, {'x' => [{'a' => ['b']}]}], document)
145
+ assert_equal([{'x' => [{'a' => ['b']}]}, {'x' => [{'a' => ['b']}]}], node_document)
148
146
  end
149
147
  it 'assigns, deeper' do
150
148
  node[1]['y'] = node[1]['x'][0]
151
- assert_equal([0, {'x' => [{'a' => ['b']}], 'y' => {'a' => ['b']}}], document)
149
+ assert_equal([0, {'x' => [{'a' => ['b']}], 'y' => {'a' => ['b']}}], node_document)
152
150
  end
153
151
  end
154
- describe '#document_node' do
155
- let(:document) { {'a' => {'b' => 3}} }
156
- it 'has content that is the document' do
157
- assert_equal({'a' => {'b' => 3}}, node['a'].document_node.content)
152
+ describe '#document_root_node' do
153
+ let(:node_document) { {'a' => {'b' => 3}} }
154
+ it 'has node_content that is the node_document' do
155
+ assert_equal({'a' => {'b' => 3}}, node['a'].document_root_node.node_content)
158
156
  end
159
157
  end
160
158
  describe '#parent_node' do
161
- let(:document) { {'a' => {'b' => []}} }
159
+ let(:node_document) { {'a' => {'b' => []}} }
162
160
  it 'finds a parent' do
163
161
  sub = node['a']['b']
164
- assert_equal(['a', 'b'], sub.path)
162
+ assert_equal(JSI::JSON::Pointer.new(['a', 'b']), sub.node_ptr)
165
163
  parent = sub.parent_node
166
- assert_equal(['a'], parent.path)
167
- assert_equal({'b' => []}, parent.content)
164
+ assert_equal(JSI::JSON::Pointer.new(['a']), parent.node_ptr)
165
+ assert_equal({'b' => []}, parent.node_content)
168
166
  assert_equal(node['a'], parent)
169
167
  root_from_sub = sub.parent_node.parent_node
170
- assert_equal([], root_from_sub.path)
171
- assert_equal({'a' => {'b' => []}}, root_from_sub.content)
168
+ assert_equal(JSI::JSON::Pointer.new([]), root_from_sub.node_ptr)
169
+ assert_equal({'a' => {'b' => []}}, root_from_sub.node_content)
172
170
  assert_equal(node, root_from_sub)
173
- err = assert_raises(::JSON::Schema::Pointer::ReferenceError) do
171
+ err = assert_raises(JSI::JSON::Pointer::ReferenceError) do
174
172
  root_from_sub.parent_node
175
173
  end
176
- assert_match(/\Acannot access parent of root node: #\{<JSI::JSON::HashNode/, err.message)
177
- end
178
- end
179
- describe '#pointer_path' do
180
- let(:document) { {'a' => {'b' => 3}} }
181
- it 'is empty' do
182
- assert_equal('', node.pointer_path)
183
- end
184
- it 'is not empty' do
185
- assert_equal('/a', node['a'].pointer_path)
186
- end
187
- describe 'containing an empty string and some slashes and tildes that need escaping' do
188
- let(:document) { {'' => {'a/b~c!d#e[f]' => []}} }
189
- it 'matches' do
190
- assert_equal('//a~1b~0c!d#e[f]', node['']['a/b~c!d#e[f]'].pointer_path)
191
- end
192
- end
193
- end
194
- describe '#fragment' do
195
- let(:document) { {'a' => {'b' => 3}} }
196
- it 'is empty' do
197
- assert_equal('#', node.fragment)
198
- end
199
- it 'is not empty' do
200
- assert_equal('#/a', node['a'].fragment)
201
- end
202
- describe 'containing an empty string and some slashes and tildes that need escaping' do
203
- let(:document) { {'' => {'a/b~c!d#e[f]' => []}} }
204
- it 'matches' do
205
- assert_equal('#//a~1b~0c!d#e%5Bf%5D', node['']['a/b~c!d#e[f]'].fragment)
206
- end
174
+ assert_equal('cannot access parent of root pointer: JSI::JSON::Pointer[]', err.message)
207
175
  end
208
176
  end
209
177
  describe '#modified_copy' do
210
- let(:document) { [['b', 'q'], {'c' => ['d', 'e']}] }
178
+ let(:node_document) { [['b', 'q'], {'c' => ['d', 'e']}] }
211
179
  let(:path) { ['1', 'c'] }
212
180
  it 'returns a different object' do
213
181
  # simplest thing
214
182
  modified_dup = node.modified_copy(&:dup)
215
183
  # it is equal - being a dup
216
- assert_equal(modified_dup, node)
184
+ assert_equal(node, modified_dup)
217
185
  # but different object
218
186
  refute_equal(node.object_id, modified_dup.object_id)
219
187
  # the parents, obviously, are different
220
- refute_equal(node.parent_node.content.object_id, modified_dup.parent_node.content.object_id)
221
- refute_equal(node.parent_node.parent_node.content.object_id, modified_dup.parent_node.parent_node.content.object_id)
222
- # but any untouched part(s) - in this case the ['b', 'q'] at document[0] - are untouched
223
- assert_equal(node.document_node[0].content.object_id, modified_dup.document_node[0].content.object_id)
188
+ refute_equal(node.parent_node.node_content.object_id, modified_dup.parent_node.node_content.object_id)
189
+ refute_equal(node.parent_node.parent_node.node_content.object_id, modified_dup.parent_node.parent_node.node_content.object_id)
190
+ # but any untouched part(s) - in this case the ['b', 'q'] at node_document[0] - are untouched
191
+ assert_equal(node.document_root_node[0].node_content.object_id, modified_dup.document_root_node[0].node_content.object_id)
224
192
  end
225
193
  it 'returns the same object' do
226
194
  unmodified_dup = node.modified_copy { |o| o }
@@ -228,59 +196,60 @@ describe JSI::JSON::Node do
228
196
  # same object, since the block just returned it
229
197
  refute_equal(node.object_id, unmodified_dup.object_id)
230
198
  # the parents are unchanged since the object is the same
231
- assert_equal(node.parent_node.content.object_id, unmodified_dup.parent_node.content.object_id)
232
- assert_equal(node.parent_node.parent_node.content.object_id, unmodified_dup.parent_node.parent_node.content.object_id)
233
- # same as the other: any untouched part(s) - in this case the ['b', 'q'] at document[0] - are untouched
234
- assert_equal(node.document_node[0].content.object_id, unmodified_dup.document_node[0].content.object_id)
199
+ assert_equal(node.parent_node.node_content.object_id, unmodified_dup.parent_node.node_content.object_id)
200
+ assert_equal(node.parent_node.parent_node.node_content.object_id, unmodified_dup.parent_node.parent_node.node_content.object_id)
201
+ # same as the other: any untouched part(s) - in this case the ['b', 'q'] at node_document[0] - are untouched
202
+ assert_equal(node.document_root_node[0].node_content.object_id, unmodified_dup.document_root_node[0].node_content.object_id)
235
203
  end
236
204
  it 'raises subscripting string from array' do
237
- err = assert_raises(TypeError) { JSI::JSON::Node.new(document, ['x']).modified_copy(&:dup) }
205
+ err = assert_raises(TypeError) { JSI::JSON::Node.new(node_document, JSI::JSON::Pointer.new(['x'])).modified_copy(&:dup) }
238
206
  assert_match(%r(\Abad subscript "x" with remaining subpath: \[\] for array: \[.*\]\z)m, err.message)
239
207
  end
240
208
  it 'raises subscripting from invalid subpath' do
241
- err = assert_raises(TypeError) { JSI::JSON::Node.new(document, [0, 0, 'what']).modified_copy(&:dup) }
209
+ err = assert_raises(TypeError) { JSI::JSON::Node.new(node_document, JSI::JSON::Pointer.new([0, 0, 'what'])).modified_copy(&:dup) }
242
210
  assert_match(%r(bad subscript: "what" with remaining subpath: \[\] for content: "b"\z)m, err.message)
243
211
  end
244
212
  end
245
213
  describe '#inspect' do
246
- let(:document) { {'a' => {'c' => ['d', 'e']}} }
214
+ let(:node_document) { {'a' => {'c' => ['d', 'e']}} }
247
215
  let(:path) { ['a'] }
248
216
  it 'inspects' do
249
- assert_equal(%Q(#<JSI::JSON::Node fragment="#/a" {"c"=>["d", "e"]}>), node.inspect)
217
+ assert_equal(%Q(#<JSI::JSON::Node #/a {"c"=>["d", "e"]}>), node.inspect)
250
218
  end
251
219
  end
252
220
  describe '#pretty_print' do
253
- let(:document) { {'a' => {'c' => ['d', 'e']}} }
221
+ let(:node_document) { {'a' => {'c' => ['d', 'e']}} }
254
222
  let(:path) { ['a'] }
255
223
  it 'pretty prints' do
256
- assert_equal(%Q(#<JSI::JSON::Node fragment="#/a" {"c"=>["d", "e"]}>), node.pretty_inspect.chomp)
224
+ assert_equal(%Q(#<JSI::JSON::Node #/a {"c"=>["d", "e"]}>), node.pretty_inspect.chomp)
257
225
  end
258
226
  end
259
- describe '#fingerprint' do
227
+ describe '#jsi_fingerprint' do
228
+ let(:node_ptr) { JSI::JSON::Pointer.new([]) }
260
229
  it 'hashes consistently' do
261
- assert_equal('x', {JSI::JSON::Node.new([0], []) => 'x'}[JSI::JSON::Node.new([0], [])])
230
+ assert_equal('x', {JSI::JSON::Node.new([0], node_ptr) => 'x'}[JSI::JSON::Node.new([0], node_ptr)])
262
231
  end
263
232
  it 'hashes consistently regardless of the Node being decorated as a subclass' do
264
- assert_equal('x', {JSI::JSON::Node.new_doc([0]) => 'x'}[JSI::JSON::Node.new([0], [])])
265
- assert_equal('x', {JSI::JSON::Node.new([0], []) => 'x'}[JSI::JSON::Node.new_doc([0])])
233
+ assert_equal('x', {JSI::JSON::Node.new_doc([0]) => 'x'}[JSI::JSON::Node.new([0], node_ptr)])
234
+ assert_equal('x', {JSI::JSON::Node.new([0], node_ptr) => 'x'}[JSI::JSON::Node.new_doc([0])])
266
235
  end
267
236
  it '==' do
268
- assert_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new([0], []))
269
- assert_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new([0], []))
270
- assert_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new_doc([0]))
237
+ assert_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new([0], node_ptr))
238
+ assert_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new([0], node_ptr))
239
+ assert_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new_doc([0]))
271
240
  assert_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new_doc([0]))
272
241
  end
273
242
  it '!=' do
274
- refute_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new({}, []))
275
- refute_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new({}, []))
276
- refute_equal(JSI::JSON::Node.new([0], []), JSI::JSON::Node.new_doc({}))
243
+ refute_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new({}, node_ptr))
244
+ refute_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new({}, node_ptr))
245
+ refute_equal(JSI::JSON::Node.new([0], node_ptr), JSI::JSON::Node.new_doc({}))
277
246
  refute_equal(JSI::JSON::Node.new_doc([0]), JSI::JSON::Node.new_doc({}))
278
247
  refute_equal({}, JSI::JSON::Node.new_doc({}))
279
248
  refute_equal(JSI::JSON::Node.new_doc({}), {})
280
249
  end
281
250
  end
282
251
  describe '#as_json' do
283
- let(:document) { {'a' => 'b'} }
252
+ let(:node_document) { {'a' => 'b'} }
284
253
  it '#as_json' do
285
254
  assert_equal({'a' => 'b'}, node.as_json)
286
255
  end