jsi 0.2.0 → 0.6.0

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