jsi 0.2.0 → 0.6.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +36 -0
  4. data/LICENSE.md +613 -0
  5. data/README.md +153 -52
  6. data/lib/jsi/base.rb +485 -338
  7. data/lib/jsi/jsi_coder.rb +24 -18
  8. data/lib/jsi/metaschema.rb +7 -0
  9. data/lib/jsi/metaschema_node/bootstrap_schema.rb +100 -0
  10. data/lib/jsi/metaschema_node.rb +245 -0
  11. data/lib/jsi/pathed_node.rb +49 -46
  12. data/lib/jsi/ptr.rb +292 -0
  13. data/lib/jsi/schema/application/child_application/contains.rb +16 -0
  14. data/lib/jsi/schema/application/child_application/draft04.rb +22 -0
  15. data/lib/jsi/schema/application/child_application/draft06.rb +29 -0
  16. data/lib/jsi/schema/application/child_application/draft07.rb +29 -0
  17. data/lib/jsi/schema/application/child_application/items.rb +18 -0
  18. data/lib/jsi/schema/application/child_application/properties.rb +25 -0
  19. data/lib/jsi/schema/application/child_application.rb +40 -0
  20. data/lib/jsi/schema/application/draft04.rb +8 -0
  21. data/lib/jsi/schema/application/draft06.rb +8 -0
  22. data/lib/jsi/schema/application/draft07.rb +8 -0
  23. data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
  24. data/lib/jsi/schema/application/inplace_application/draft04.rb +26 -0
  25. data/lib/jsi/schema/application/inplace_application/draft06.rb +27 -0
  26. data/lib/jsi/schema/application/inplace_application/draft07.rb +33 -0
  27. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
  28. data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
  29. data/lib/jsi/schema/application/inplace_application/someof.rb +29 -0
  30. data/lib/jsi/schema/application/inplace_application.rb +46 -0
  31. data/lib/jsi/schema/application.rb +12 -0
  32. data/lib/jsi/schema/draft04.rb +14 -0
  33. data/lib/jsi/schema/draft06.rb +14 -0
  34. data/lib/jsi/schema/draft07.rb +14 -0
  35. data/lib/jsi/schema/issue.rb +36 -0
  36. data/lib/jsi/schema/ref.rb +159 -0
  37. data/lib/jsi/schema/schema_ancestor_node.rb +119 -0
  38. data/lib/jsi/schema/validation/array.rb +69 -0
  39. data/lib/jsi/schema/validation/const.rb +20 -0
  40. data/lib/jsi/schema/validation/contains.rb +25 -0
  41. data/lib/jsi/schema/validation/core.rb +39 -0
  42. data/lib/jsi/schema/validation/dependencies.rb +49 -0
  43. data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
  44. data/lib/jsi/schema/validation/draft04.rb +112 -0
  45. data/lib/jsi/schema/validation/draft06.rb +122 -0
  46. data/lib/jsi/schema/validation/draft07.rb +159 -0
  47. data/lib/jsi/schema/validation/enum.rb +25 -0
  48. data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
  49. data/lib/jsi/schema/validation/items.rb +54 -0
  50. data/lib/jsi/schema/validation/not.rb +20 -0
  51. data/lib/jsi/schema/validation/numeric.rb +121 -0
  52. data/lib/jsi/schema/validation/object.rb +45 -0
  53. data/lib/jsi/schema/validation/pattern.rb +34 -0
  54. data/lib/jsi/schema/validation/properties.rb +101 -0
  55. data/lib/jsi/schema/validation/property_names.rb +32 -0
  56. data/lib/jsi/schema/validation/ref.rb +40 -0
  57. data/lib/jsi/schema/validation/required.rb +27 -0
  58. data/lib/jsi/schema/validation/someof.rb +90 -0
  59. data/lib/jsi/schema/validation/string.rb +47 -0
  60. data/lib/jsi/schema/validation/type.rb +49 -0
  61. data/lib/jsi/schema/validation.rb +51 -0
  62. data/lib/jsi/schema.rb +528 -233
  63. data/lib/jsi/schema_classes.rb +238 -51
  64. data/lib/jsi/schema_registry.rb +141 -0
  65. data/lib/jsi/schema_set.rb +141 -0
  66. data/lib/jsi/simple_wrap.rb +8 -3
  67. data/lib/jsi/typelike_modules.rb +75 -68
  68. data/lib/jsi/util/attr_struct.rb +106 -0
  69. data/lib/jsi/util.rb +167 -64
  70. data/lib/jsi/validation/error.rb +34 -0
  71. data/lib/jsi/validation/result.rb +210 -0
  72. data/lib/jsi/validation.rb +15 -0
  73. data/lib/jsi/version.rb +3 -1
  74. data/lib/jsi.rb +72 -9
  75. data/lib/schemas/json-schema.org/draft-04/schema.rb +12 -0
  76. data/lib/schemas/json-schema.org/draft-06/schema.rb +12 -0
  77. data/lib/schemas/json-schema.org/draft-07/schema.rb +12 -0
  78. data/readme.rb +138 -0
  79. data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
  80. data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
  81. data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
  82. metadata +80 -107
  83. data/.simplecov +0 -1
  84. data/LICENSE.txt +0 -21
  85. data/Rakefile.rb +0 -9
  86. data/jsi.gemspec +0 -31
  87. data/lib/jsi/base/to_rb.rb +0 -126
  88. data/lib/jsi/json/node.rb +0 -243
  89. data/lib/jsi/json/pointer.rb +0 -330
  90. data/lib/jsi/json-schema-fragments.rb +0 -59
  91. data/lib/jsi/json.rb +0 -8
  92. data/test/base_array_test.rb +0 -209
  93. data/test/base_hash_test.rb +0 -204
  94. data/test/base_test.rb +0 -422
  95. data/test/jsi_coder_test.rb +0 -85
  96. data/test/jsi_json_arraynode_test.rb +0 -150
  97. data/test/jsi_json_hashnode_test.rb +0 -132
  98. data/test/jsi_json_node_test.rb +0 -310
  99. data/test/jsi_json_pointer_test.rb +0 -106
  100. data/test/jsi_test.rb +0 -11
  101. data/test/jsi_typelike_as_json_test.rb +0 -53
  102. data/test/schema_test.rb +0 -196
  103. data/test/spreedly_openapi_test.rb +0 -8
  104. data/test/test_helper.rb +0 -63
  105. data/test/util_test.rb +0 -62
@@ -1,204 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- describe JSI::BaseHash do
4
- let(:document) do
5
- {'foo' => {'x' => 'y'}, 'bar' => [9], 'baz' => true}
6
- end
7
- let(:path) { [] }
8
- let(:pointer) { JSI::JSON::Pointer.new(path) }
9
- let(:instance) { JSI::JSON::Node.new_by_type(document, pointer) }
10
- let(:schema_content) do
11
- {
12
- 'type' => 'object',
13
- 'properties' => {
14
- 'foo' => {'type' => 'object'},
15
- 'bar' => {},
16
- },
17
- }
18
- end
19
- let(:schema) { JSI::Schema.new(schema_content) }
20
- let(:class_for_schema) { JSI.class_for_schema(schema) }
21
- let(:subject) { class_for_schema.new(instance) }
22
-
23
- describe '#[] with a default that is a basic type' do
24
- let(:schema_content) do
25
- {
26
- 'type' => 'object',
27
- 'properties' => {
28
- 'foo' => {'default' => 'foo'},
29
- },
30
- }
31
- end
32
- describe 'default value' do
33
- let(:document) { {'bar' => 3} }
34
- it 'returns the default value' do
35
- assert_equal('foo', subject.foo)
36
- end
37
- end
38
- describe 'nondefault value (basic type)' do
39
- let(:document) { {'foo' => 'who'} }
40
- it 'returns the nondefault value' do
41
- assert_equal('who', subject.foo)
42
- end
43
- end
44
- describe 'nondefault value (nonbasic type)' do
45
- let(:document) { {'foo' => [2]} }
46
- it 'returns the nondefault value' do
47
- assert_instance_of(JSI.class_for_schema(schema['properties']['foo']), subject.foo)
48
- assert_equal([2], subject.foo.as_json)
49
- end
50
- end
51
- end
52
- describe '#[] with a default that is a nonbasic type' do
53
- let(:schema_content) do
54
- {
55
- 'type' => 'object',
56
- 'properties' => {
57
- 'foo' => {'default' => {'foo' => 2}},
58
- },
59
- }
60
- end
61
- describe 'default value' do
62
- let(:document) { {'bar' => 3} }
63
- it 'returns the default value' do
64
- assert_instance_of(JSI.class_for_schema(schema['properties']['foo']), subject.foo)
65
- assert_equal({'foo' => 2}, subject.foo.as_json)
66
- end
67
- end
68
- describe 'nondefault value (basic type)' do
69
- let(:document) { {'foo' => 'who'} }
70
- it 'returns the nondefault value' do
71
- assert_equal('who', subject.foo)
72
- end
73
- end
74
- describe 'nondefault value (nonbasic type)' do
75
- let(:document) { {'foo' => [2]} }
76
- it 'returns the nondefault value' do
77
- assert_instance_of(JSI.class_for_schema(schema['properties']['foo']), subject.foo)
78
- assert_equal([2], subject.foo.as_json)
79
- end
80
- end
81
- end
82
- describe 'hashlike []=' do
83
- it 'sets a property' do
84
- orig_foo = subject['foo']
85
-
86
- subject['foo'] = {'y' => 'z'}
87
-
88
- assert_equal({'y' => 'z'}, subject['foo'].as_json)
89
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
90
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject['foo'])
91
- end
92
- it 'sets a property to a schema instance with a different schema' do
93
- assert(subject['foo'])
94
-
95
- subject['foo'] = subject['bar']
96
-
97
- # the content of the subscripts' instances is the same but the subscripts' classes are different
98
- assert_equal([9], subject['foo'].as_json)
99
- assert_equal([9], subject['bar'].as_json)
100
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject['foo'])
101
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['bar']), subject['bar'])
102
- end
103
- it 'sets a property to a schema instance with the same schema' do
104
- other_subject = class_for_schema.new(JSI::JSON::Node.new_doc({'foo' => {'x' => 'y'}, 'bar' => [9], 'baz' => true}))
105
- # Given
106
- assert_equal(other_subject, subject)
107
-
108
- # When:
109
- subject['foo'] = other_subject['foo']
110
-
111
- # Then:
112
- # still equal
113
- assert_equal(other_subject, subject)
114
- # but different instances
115
- refute_equal(other_subject['foo'].object_id, subject['foo'].object_id)
116
- end
117
- it 'modifies the instance, visible to other references to the same instance' do
118
- orig_instance = subject.instance
119
-
120
- subject['foo'] = {'y' => 'z'}
121
-
122
- assert_equal(orig_instance, subject.instance)
123
- assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
124
- assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
125
- assert_equal(orig_instance.class, subject.instance.class)
126
- end
127
- describe 'when the instance is not hashlike' do
128
- let(:instance) { nil }
129
- it 'errors' do
130
- err = assert_raises(NoMethodError) { subject['foo'] = 0 }
131
- assert_match(%r(\Aundefined method `\[\]=' for #<JSI::SchemaClasses::.*>\z), err.message)
132
- end
133
- end
134
- end
135
- # these methods just delegate to Hash so not going to test excessively
136
- describe 'key only methods' do
137
- it('#each_key') { assert_equal(['foo', 'bar', 'baz'], subject.each_key.to_a) }
138
- it('#empty?') { assert_equal(false, subject.empty?) }
139
- it('#has_key?') { assert_equal(true, subject.has_key?('bar')) }
140
- it('#include?') { assert_equal(false, subject.include?('q')) }
141
- it('#key?') { assert_equal(true, subject.key?('baz')) }
142
- it('#keys') { assert_equal(['foo', 'bar', 'baz'], subject.keys) }
143
- it('#length') { assert_equal(3, subject.length) }
144
- it('#member?') { assert_equal(false, subject.member?(0)) }
145
- it('#size') { assert_equal(3, subject.size) }
146
- end
147
- describe 'key + value methods' do
148
- it('#<') { assert_equal(true, subject < {'foo' => subject['foo'], 'bar' => subject['bar'], 'baz' => true, 'x' => 'y'}) } if {}.respond_to?(:<)
149
- it('#<=') { assert_equal(true, subject <= subject) } if {}.respond_to?(:<=)
150
- it('#>') { assert_equal(true, subject > {}) } if {}.respond_to?(:>)
151
- it('#>=') { assert_equal(false, subject >= {'foo' => 'bar'}) } if {}.respond_to?(:>=)
152
- it('#any?') { assert_equal(false, subject.any? { |k, v| v == 3 }) }
153
- it('#assoc') { assert_equal(['foo', subject['foo']], subject.assoc('foo')) }
154
- it('#dig') { assert_equal(9, subject.dig('bar', 0)) } if {}.respond_to?(:dig)
155
- it('#each_pair') { assert_equal([['foo', subject['foo']], ['bar', subject['bar']], ['baz', true]], subject.each_pair.to_a) }
156
- it('#each_value') { assert_equal([subject['foo'], subject['bar'], true], subject.each_value.to_a) }
157
- it('#fetch') { assert_equal(true, subject.fetch('baz')) }
158
- it('#fetch_values') { assert_equal([true], subject.fetch_values('baz')) } if {}.respond_to?(:fetch_values)
159
- it('#has_value?') { assert_equal(true, subject.has_value?(true)) }
160
- it('#invert') { assert_equal({subject['foo'] => 'foo', subject['bar'] => 'bar', true => 'baz'}, subject.invert) }
161
- it('#key') { assert_equal('baz', subject.key(true)) }
162
- it('#rassoc') { assert_equal(['baz', true], subject.rassoc(true)) }
163
- it('#to_h') { assert_equal({'foo' => subject['foo'], 'bar' => subject['bar'], 'baz' => true}, subject.to_h) }
164
- it('#to_proc') { assert_equal(true, subject.to_proc.call('baz')) } if {}.respond_to?(:to_proc)
165
- if {}.respond_to?(:transform_values)
166
- it('#transform_values') { assert_equal({'foo' => nil, 'bar' => nil, 'baz' => nil}, subject.transform_values { |_| nil }) }
167
- end
168
- it('#value?') { assert_equal(false, subject.value?('0')) }
169
- it('#values') { assert_equal([subject['foo'], subject['bar'], true], subject.values) }
170
- it('#values_at') { assert_equal([true], subject.values_at('baz')) }
171
- end
172
- describe 'with an instance that has to_hash but not other hash instance methods' do
173
- let(:instance) { SortOfHash.new({'foo' => SortOfHash.new({'a' => 'b'})}) }
174
- describe 'delegating instance methods to #to_hash' do
175
- it('#each_key') { assert_equal(['foo'], subject.each_key.to_a) }
176
- it('#each_pair') { assert_equal([['foo', subject['foo']]], subject.each_pair.to_a) }
177
- it('#[]') { assert_equal(SortOfHash.new({'a' => 'b'}), subject['foo'].instance) }
178
- it('#as_json') { assert_equal({'foo' => {'a' => 'b'}}, subject.as_json) }
179
- end
180
- end
181
- describe 'modified copy methods' do
182
- # I'm going to rely on the #merge test above to test the modified copy functionality and just do basic
183
- # tests of all the modified copy methods here
184
- it('#merge') { assert_equal(subject, subject.merge({})) }
185
- it('#reject') { assert_equal(class_for_schema.new(JSI::JSON::HashNode.new({}, pointer)), subject.reject { true }) }
186
- it('#select') { assert_equal(class_for_schema.new(JSI::JSON::HashNode.new({}, pointer)), subject.select { false }) }
187
- describe '#select' do
188
- it 'yields properly too' do
189
- subject.select do |k, v|
190
- assert_equal(subject[k], v)
191
- end
192
- end
193
- end
194
- # Hash#compact only available as of ruby 2.5.0
195
- if {}.respond_to?(:compact)
196
- it('#compact') { assert_equal(subject, subject.compact) }
197
- end
198
- end
199
- JSI::Hashlike::DESTRUCTIVE_METHODS.each do |destructive_method_name|
200
- it("does not respond to destructive method #{destructive_method_name}") do
201
- assert(!subject.respond_to?(destructive_method_name))
202
- end
203
- end
204
- end
data/test/base_test.rb DELETED
@@ -1,422 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- NamedSchemaInstance = JSI.class_for_schema({id: 'https://schemas.jsi.unth.net/test/base/named_schema'})
4
-
5
- describe JSI::Base do
6
- let(:document) { {} }
7
- let(:path) { [] }
8
- let(:pointer) { JSI::JSON::Pointer.new(path) }
9
- let(:instance) { JSI::JSON::Node.new_by_type(document, pointer) }
10
- let(:schema_content) { {} }
11
- let(:schema) { JSI::Schema.new(schema_content) }
12
- let(:subject) { JSI.class_for_schema(schema).new(instance) }
13
- describe 'class .inspect + .to_s' do
14
- it 'is the same as Class#inspect on the base' do
15
- assert_equal('JSI::Base', JSI::Base.inspect)
16
- assert_equal('JSI::Base', JSI::Base.to_s)
17
- end
18
- it 'is SchemaClasses[] for generated subclass without id' do
19
- assert_match(%r(\AJSI::SchemaClasses\["[a-f0-9\-]+#"\]\z), subject.class.inspect)
20
- assert_match(%r(\AJSI::SchemaClasses\["[a-f0-9\-]+#"\]\z), subject.class.to_s)
21
- end
22
- describe 'with schema id' do
23
- let(:schema_content) { {'id' => 'https://jsi/foo'} }
24
- it 'is SchemaClasses[] for generated subclass with id' do
25
- assert_equal(%q(JSI::SchemaClasses["https://jsi/foo#"]), subject.class.inspect)
26
- assert_equal(%q(JSI::SchemaClasses["https://jsi/foo#"]), subject.class.to_s)
27
- end
28
- end
29
- it 'is the constant name (plus id for .inspect) for a class assigned to a constant' do
30
- assert_equal(%q(NamedSchemaInstance (https://schemas.jsi.unth.net/test/base/named_schema#)), NamedSchemaInstance.inspect)
31
- assert_equal(%q(NamedSchemaInstance), NamedSchemaInstance.to_s)
32
- end
33
- end
34
- describe 'class name' do
35
- let(:schema_content) { {'id' => 'https://jsi/BaseTest'} }
36
- it 'generates a class name from schema_id' do
37
- assert_equal('JSI::SchemaClasses::Https___jsi_BaseTest_', subject.class.name)
38
- end
39
- it 'uses an existing name' do
40
- assert_equal('NamedSchemaInstance', NamedSchemaInstance.name)
41
- end
42
- end
43
- describe 'class for schema .schema' do
44
- it '.schema' do
45
- assert_equal(schema, JSI.class_for_schema(schema).schema)
46
- end
47
- end
48
- describe 'class for schema .schema_id' do
49
- it '.schema_id' do
50
- assert_equal(schema.schema_id, JSI.class_for_schema(schema).schema_id)
51
- end
52
- end
53
- describe 'module for schema .inspect' do
54
- it '.inspect' do
55
- assert_match(%r(\A#<Module for Schema: .+#>\z), JSI::SchemaClasses.module_for_schema(schema).inspect)
56
- end
57
- end
58
- describe 'module for schema .schema' do
59
- it '.schema' do
60
- assert_equal(schema, JSI::SchemaClasses.module_for_schema(schema).schema)
61
- end
62
- end
63
- describe 'SchemaClasses[]' do
64
- it 'stores the class for the schema' do
65
- assert_equal(JSI.class_for_schema(schema), JSI::SchemaClasses[schema.schema_id])
66
- end
67
- end
68
- describe '.class_for_schema' do
69
- it 'returns a class from a schema' do
70
- class_for_schema = JSI.class_for_schema(schema)
71
- # same class every time
72
- assert_equal(JSI.class_for_schema(schema), class_for_schema)
73
- assert_operator(class_for_schema, :<, JSI::Base)
74
- end
75
- it 'returns a class from a hash' do
76
- assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(schema.schema_node.content))
77
- end
78
- it 'returns a class from a schema node' do
79
- assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(schema.schema_node))
80
- end
81
- it 'returns a class from a Base' do
82
- assert_equal(JSI.class_for_schema(schema), JSI.class_for_schema(JSI.class_for_schema({}).new(schema.schema_node)))
83
- end
84
- end
85
- describe 'JSI::SchemaClasses.module_for_schema' do
86
- it 'returns a module from a schema' do
87
- module_for_schema = JSI::SchemaClasses.module_for_schema(schema)
88
- # same module every time
89
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), module_for_schema)
90
- end
91
- it 'returns a module from a hash' do
92
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.schema_node.content))
93
- end
94
- it 'returns a module from a schema node' do
95
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(schema.schema_node))
96
- end
97
- it 'returns a module from a Base' do
98
- assert_equal(JSI::SchemaClasses.module_for_schema(schema), JSI::SchemaClasses.module_for_schema(JSI.class_for_schema({}).new(schema.schema_node)))
99
- end
100
- end
101
- describe 'initialization' do
102
- describe 'on Base' do
103
- it 'errors' do
104
- err = assert_raises(TypeError) { JSI::Base.new({}) }
105
- assert_equal('cannot instantiate JSI::Base which has no method #schema. please use JSI.class_for_schema', err.message)
106
- end
107
- end
108
- describe 'nil' do
109
- let(:instance) { nil }
110
- it 'initializes with nil instance' do
111
- assert_equal(nil, subject.instance)
112
- assert(!subject.respond_to?(:to_ary))
113
- assert(!subject.respond_to?(:to_hash))
114
- end
115
- end
116
- describe 'arbitrary instance' do
117
- let(:instance) { Object.new }
118
- it 'initializes' do
119
- assert_equal(instance, subject.instance)
120
- assert(!subject.respond_to?(:to_ary))
121
- assert(!subject.respond_to?(:to_hash))
122
- end
123
- end
124
- describe 'hash' do
125
- let(:instance) { {'foo' => 'bar'} }
126
- let(:schema_content) { {'type' => 'object'} }
127
- it 'initializes' do
128
- assert_equal({'foo' => 'bar'}, subject.instance)
129
- assert(!subject.respond_to?(:to_ary))
130
- assert(subject.respond_to?(:to_hash))
131
- end
132
- end
133
- describe 'JSI::JSON::Hashnode' do
134
- let(:document) { {'foo' => 'bar'} }
135
- let(:schema_content) { {'type' => 'object'} }
136
- it 'initializes' do
137
- assert_equal(JSI::JSON::HashNode.new({'foo' => 'bar'}, JSI::JSON::Pointer.new([])), subject.instance)
138
- assert(!subject.respond_to?(:to_ary))
139
- assert(subject.respond_to?(:to_hash))
140
- end
141
- end
142
- describe 'array' do
143
- let(:instance) { ['foo'] }
144
- let(:schema_content) { {'type' => 'array'} }
145
- it 'initializes' do
146
- assert_equal(['foo'], subject.instance)
147
- assert(subject.respond_to?(:to_ary))
148
- assert(!subject.respond_to?(:to_hash))
149
- end
150
- end
151
- describe 'JSI::JSON::Arraynode' do
152
- let(:document) { ['foo'] }
153
- let(:schema_content) { {'type' => 'array'} }
154
- it 'initializes' do
155
- assert_equal(JSI::JSON::ArrayNode.new(['foo'], JSI::JSON::Pointer.new([])), subject.instance)
156
- assert(subject.respond_to?(:to_ary))
157
- assert(!subject.respond_to?(:to_hash))
158
- end
159
- end
160
- describe 'another JSI::Base invalid' do
161
- let(:schema_content) { {'type' => 'object'} }
162
- let(:instance) { JSI.class_for_schema(schema).new({'foo' => 'bar'}) }
163
- it 'initializes with an error' do
164
- err = assert_raises(TypeError) { subject }
165
- assert_match(%r(\Aassigning another JSI::Base instance to JSI::SchemaClasses\[\".*#\"\] instance is incorrect. received: #\{<JSI::SchemaClasses\[.*\] Hash>\s*"foo" => "bar"\s*\}\z)m, err.message)
166
- end
167
- end
168
- describe 'Schema invalid' do
169
- let(:instance) { JSI::Schema.new({}) }
170
- it 'initializes with an error' do
171
- err = assert_raises(TypeError) { subject }
172
- assert_match(%r(\Aassigning a schema to JSI::SchemaClasses\[\".*#\"\] instance is incorrect. received: #<JSI::Schema schema_id=.*>\z)m, err.message)
173
- end
174
- end
175
- end
176
- describe '#parent_jsis, #parent_jsi' do
177
- let(:schema_content) { {'properties' => {'foo' => {'properties' => {'bar' => {'properties' => {'baz' => {}}}}}}} }
178
- let(:document) { {'foo' => {'bar' => {'baz' => {}}}} }
179
- describe 'no parent_jsis' do
180
- it 'has none' do
181
- assert_equal([], subject.parents)
182
- assert_equal([], subject.parent_jsis.to_a)
183
- assert_equal(nil, subject.parent)
184
- assert_equal(nil, subject.parent_jsi)
185
- end
186
- end
187
- describe 'one parent_jsi' do
188
- it 'has one' do
189
- assert_equal([subject], subject.foo.parents)
190
- assert_equal([subject], subject.foo.parent_jsis.to_a)
191
- assert_equal(subject, subject.foo.parent)
192
- assert_equal(subject, subject.foo.parent_jsi)
193
- end
194
- end
195
- describe 'more parent_jsis' do
196
- it 'has more' do
197
- assert_equal([subject.foo.bar, subject.foo, subject], subject.foo.bar.baz.parents)
198
- assert_equal([subject.foo.bar, subject.foo, subject], subject.foo.bar.baz.parent_jsis.to_a)
199
- assert_equal(subject.foo.bar, subject.foo.bar.baz.parent)
200
- assert_equal(subject.foo.bar, subject.foo.bar.baz.parent_jsi)
201
- end
202
- end
203
- end
204
- describe '#each, Enumerable methods' do
205
- let(:document) { 'a string' }
206
- it "raises NoMethodError calling each or Enumerable methods" do
207
- assert_raises(NoMethodError) { subject.each { nil } }
208
- assert_raises(NoMethodError) { subject.map { nil } }
209
- end
210
- end
211
- describe '#modified_copy' do
212
- describe 'with an instance that does not have #modified_copy' do
213
- let(:instance) { Object.new }
214
- it 'yields the instance to modify' do
215
- new_instance = Object.new
216
- modified = subject.modified_copy do |o|
217
- assert_equal(instance, o)
218
- new_instance
219
- end
220
- assert_equal(new_instance, modified.instance)
221
- assert_equal(instance, subject.instance)
222
- refute_equal(instance, modified)
223
- end
224
- end
225
- describe 'with an instance that does have #modified_copy' do
226
- it 'yields the instance to modify' do
227
- modified = subject.modified_copy do |o|
228
- assert_equal({}, o)
229
- {'a' => 'b'}
230
- end
231
- assert_equal({'a' => 'b'}, modified.instance.content)
232
- assert_equal({}, subject.instance.content)
233
- refute_equal(instance, modified)
234
- end
235
- end
236
- describe 'no modification' do
237
- it 'yields the instance to modify' do
238
- modified = subject.modified_copy { |o| o }
239
- # this doesn't really need to be tested but ... whatever
240
- assert_equal(subject.instance.content.object_id, modified.instance.content.object_id)
241
- assert_equal(subject, modified)
242
- refute_equal(subject.object_id, modified.object_id)
243
- end
244
- end
245
- describe 'resulting in a different type' do
246
- let(:schema_content) { {'type' => 'object'} }
247
- it 'works' do
248
- # I'm not really sure the best thing to do here, but this is how it is for now. this is subject to change.
249
- modified = subject.modified_copy do |o|
250
- o.to_s
251
- end
252
- assert_equal('{}', modified.instance.content)
253
- assert_equal({}, subject.instance.content)
254
- refute_equal(instance, modified)
255
- # interesting side effect
256
- assert(subject.respond_to?(:to_hash))
257
- assert(!modified.respond_to?(:to_hash))
258
- assert_equal(JSI::JSON::HashNode, subject.instance.class)
259
- assert_equal(JSI::JSON::Node, modified.instance.class)
260
- end
261
- end
262
- end
263
- it('#fragment') { assert_equal('#', subject.fragment) }
264
- describe 'validation' do
265
- describe 'without errors' do
266
- it '#fully_validate' do
267
- assert_equal([], subject.fully_validate)
268
- end
269
- it '#validate' do
270
- assert_equal(true, subject.validate)
271
- end
272
- it '#validate!' do
273
- assert_equal(true, subject.validate!)
274
- end
275
- end
276
- end
277
- describe 'property accessors' do
278
- let(:schema_content) do
279
- {
280
- 'type' => 'object',
281
- 'properties' => {
282
- 'foo' => {'type' => 'object'},
283
- 'bar' => {'type' => 'array'},
284
- 'baz' => {},
285
- },
286
- }
287
- end
288
- let(:document) do
289
- {'foo' => {'x' => 'y'}, 'bar' => [3.14159], 'baz' => true, 'qux' => []}
290
- end
291
- describe 'readers' do
292
- it 'reads attributes described as properties' do
293
- assert_equal({'x' => 'y'}, subject.foo.as_json)
294
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
295
- assert_respond_to(subject.foo, :to_hash)
296
- refute_respond_to(subject.foo, :to_ary)
297
- assert_equal([3.14159], subject.bar.as_json)
298
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['bar']), subject.bar)
299
- refute_respond_to(subject.bar, :to_hash)
300
- assert_respond_to(subject.bar, :to_ary)
301
- assert_equal(true, subject.baz)
302
- refute_respond_to(subject.baz, :to_hash)
303
- refute_respond_to(subject.baz, :to_ary)
304
- refute_respond_to(subject, :qux)
305
- end
306
- describe 'when the instance is not hashlike' do
307
- let(:instance) { nil }
308
- it 'errors' do
309
- err = assert_raises(NoMethodError) { subject.foo }
310
- assert_match(%r(\Aschema instance of class .* does not respond to \[\]; cannot call reader 'foo'. instance is )m, err.message)
311
- end
312
- end
313
- describe 'properties with the same names as instance methods' do
314
- let(:schema_content) do
315
- {
316
- 'type' => 'object',
317
- 'properties' => {
318
- 'foo' => {}, # not an instance method
319
- 'initialize' => {}, # Base
320
- 'inspect' => {}, # Base
321
- 'pretty_inspect' => {}, # Kernel
322
- 'as_json' => {}, # Base::OverrideFromExtensions, extended on initialization
323
- 'each' => {}, # BaseHash / BaseArray
324
- 'instance_exec' => {}, # BasicObject
325
- 'instance' => {}, # Base
326
- 'schema' => {}, # module_for_schema singleton definition
327
- },
328
- }
329
- end
330
- let(:document) do
331
- {
332
- 'foo' => 'bar',
333
- 'initialize' => 'hi',
334
- 'inspect' => 'hi',
335
- 'pretty_inspect' => 'hi',
336
- 'as_json' => 'hi',
337
- 'each' => 'hi',
338
- 'instance_exec' => 'hi',
339
- 'instance' => 'hi',
340
- 'schema' => 'hi',
341
- }
342
- end
343
- it 'does not define readers' do
344
- assert_equal('bar', subject.foo)
345
- assert_equal(JSI::SchemaClasses.module_for_schema(subject.schema, conflicting_modules: [JSI::Base, JSI::BaseArray, JSI::BaseHash]), subject.method(:foo).owner)
346
-
347
- assert_equal(JSI::Base, subject.method(:initialize).owner)
348
- assert_equal('hi', subject['initialize'])
349
- assert_match(%r(\A#\{<JSI::SchemaClasses\[".*#"\].*}\z)m, subject.inspect)
350
- assert_equal('hi', subject['inspect'])
351
- assert_match(%r(\A#\{<JSI::SchemaClasses\[".*#"\].*}\Z)m, subject.pretty_inspect)
352
- assert_equal(document, subject.as_json)
353
- assert_equal(subject, subject.each { })
354
- assert_equal(2, subject.instance_exec { 2 })
355
- assert_equal(instance, subject.instance)
356
- assert_equal(schema, subject.schema)
357
- end
358
- end
359
- end
360
- describe 'writers' do
361
- it 'writes attributes described as properties' do
362
- orig_foo = subject.foo
363
-
364
- subject.foo = {'y' => 'z'}
365
-
366
- assert_equal({'y' => 'z'}, subject.foo.as_json)
367
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
368
- assert_instance_of(JSI.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
369
- end
370
- it 'modifies the instance, visible to other references to the same instance' do
371
- orig_instance = subject.instance
372
-
373
- subject.foo = {'y' => 'z'}
374
-
375
- assert_equal(orig_instance, subject.instance)
376
- assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
377
- assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
378
- assert_equal(orig_instance.class, subject.instance.class)
379
- end
380
- describe 'when the instance is not hashlike' do
381
- let(:instance) { nil }
382
- it 'errors' do
383
- err = assert_raises(NoMethodError) { subject.foo = 0 }
384
- assert_match(%r(\Aschema instance of class .* does not respond to \[\]=; cannot call writer 'foo='. instance is )m, err.message)
385
- end
386
- end
387
- end
388
- end
389
- describe '#inspect' do
390
- # if the instance is hash-like, #inspect gets overridden
391
- let(:document) { Object.new }
392
- it 'inspects' do
393
- assert_match(%r(\A#<JSI::SchemaClasses\["[^"]+#"\] #<JSI::JSON::Node fragment="#" #<Object:[^<>]*>>>\z), subject.inspect)
394
- end
395
- end
396
- describe '#pretty_print' do
397
- # if the instance is hash-like, #pretty_print gets overridden
398
- let(:document) { Object.new }
399
- it 'pretty_prints' do
400
- assert_match(%r(\A#<JSI::SchemaClasses\["[^"]+#"\]\n #<JSI::JSON::Node fragment="#" #<Object:[^<>]*>>\n>\z), subject.pretty_inspect.chomp)
401
- end
402
- end
403
- describe '#as_json' do
404
- it '#as_json' do
405
- assert_equal({'a' => 'b'}, JSI.class_for_schema({}).new(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
406
- assert_equal({'a' => 'b'}, JSI.class_for_schema({'type' => 'object'}).new(JSI::JSON::Node.new_doc({'a' => 'b'})).as_json)
407
- assert_equal(['a', 'b'], JSI.class_for_schema({'type' => 'array'}).new(JSI::JSON::Node.new_doc(['a', 'b'])).as_json)
408
- assert_equal(['a'], JSI.class_for_schema({}).new(['a']).as_json(some_option: true))
409
- end
410
- end
411
- describe 'equality between different classes of JSI::Base subclasses' do
412
- let(:subject_subclass) { Class.new(JSI.class_for_schema(schema)).new(instance) }
413
-
414
- it 'considers a Base subclass (class_for_schema) and subsubclass to be equal with the same instance' do
415
- assert_equal(subject.hash, subject_subclass.hash)
416
- assert(subject == subject_subclass)
417
- assert(subject_subclass == subject)
418
- assert(subject.eql?(subject_subclass))
419
- assert(subject_subclass.eql?(subject))
420
- end
421
- end
422
- end