jsi 0.0.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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