hocon 0.9.5 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -2
  3. data/README.md +22 -10
  4. data/lib/hocon.rb +9 -3
  5. data/lib/hocon/config_factory.rb +4 -0
  6. data/lib/hocon/config_value_factory.rb +13 -2
  7. data/lib/hocon/impl/config_reference.rb +5 -2
  8. data/lib/hocon/impl/simple_config_origin.rb +1 -1
  9. data/spec/fixtures/parse_render/example1/input.conf +21 -0
  10. data/spec/fixtures/parse_render/example1/output.conf +26 -0
  11. data/spec/fixtures/parse_render/example1/output_nocomments.conf +17 -0
  12. data/spec/fixtures/parse_render/example2/input.conf +10 -0
  13. data/spec/fixtures/parse_render/example2/output.conf +17 -0
  14. data/spec/fixtures/parse_render/example2/output_nocomments.conf +17 -0
  15. data/spec/fixtures/parse_render/example3/input.conf +2 -0
  16. data/spec/fixtures/parse_render/example3/output.conf +2 -0
  17. data/spec/fixtures/parse_render/example4/input.json +6 -0
  18. data/spec/fixtures/parse_render/example4/output.conf +6 -0
  19. data/spec/fixtures/test_utils/resources/bom.conf +2 -0
  20. data/spec/fixtures/test_utils/resources/cycle.conf +1 -0
  21. data/spec/fixtures/test_utils/resources/file-include.conf +5 -0
  22. data/spec/fixtures/test_utils/resources/include-from-list.conf +4 -0
  23. data/spec/fixtures/test_utils/resources/subdir/bar.conf +1 -0
  24. data/spec/fixtures/test_utils/resources/subdir/baz.conf +1 -0
  25. data/spec/fixtures/test_utils/resources/subdir/foo.conf +5 -0
  26. data/spec/fixtures/test_utils/resources/test01.conf +80 -0
  27. data/spec/fixtures/test_utils/resources/test01.json +4 -0
  28. data/spec/fixtures/test_utils/resources/test03.conf +36 -0
  29. data/spec/spec_helper.rb +43 -0
  30. data/spec/test_utils.rb +757 -0
  31. data/spec/unit/typesafe/config/concatenation_spec.rb +417 -0
  32. data/spec/unit/typesafe/config/conf_parser_spec.rb +822 -0
  33. data/spec/unit/typesafe/config/config_document_parser_spec.rb +494 -0
  34. data/spec/unit/typesafe/config/config_document_spec.rb +576 -0
  35. data/spec/unit/typesafe/config/config_factory_spec.rb +120 -0
  36. data/spec/unit/typesafe/config/config_node_spec.rb +552 -0
  37. data/spec/unit/typesafe/config/config_value_factory_spec.rb +85 -0
  38. data/spec/unit/typesafe/config/config_value_spec.rb +935 -0
  39. data/spec/unit/typesafe/config/hocon_spec.rb +54 -0
  40. data/spec/unit/typesafe/config/path_spec.rb +261 -0
  41. data/spec/unit/typesafe/config/public_api_spec.rb +520 -0
  42. data/spec/unit/typesafe/config/simple_config_spec.rb +112 -0
  43. data/spec/unit/typesafe/config/token_spec.rb +188 -0
  44. data/spec/unit/typesafe/config/tokenizer_spec.rb +801 -0
  45. metadata +39 -3
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'hocon/config_value_factory'
5
+ require 'hocon/config_render_options'
6
+ require 'hocon/config_error'
7
+
8
+ describe Hocon::ConfigValueFactory do
9
+ let(:render_options) { Hocon::ConfigRenderOptions.defaults }
10
+
11
+ before do
12
+ render_options.origin_comments = false
13
+ render_options.json = false
14
+ end
15
+
16
+ context "converting objects to ConfigValue using ConfigValueFactory" do
17
+ it "should convert true into a ConfigBoolean" do
18
+ value = Hocon::ConfigValueFactory.from_any_ref(true, nil)
19
+ expect(value).to be_instance_of(Hocon::Impl::ConfigBoolean)
20
+ expect(value.unwrapped).to eql(true)
21
+ end
22
+
23
+ it "should convert false into a ConfigBoolean" do
24
+ value = Hocon::ConfigValueFactory.from_any_ref(false, nil)
25
+ expect(value).to be_instance_of(Hocon::Impl::ConfigBoolean)
26
+ expect(value.unwrapped).to eql(false)
27
+ end
28
+
29
+ it "should convert nil into a ConfigNull object" do
30
+ value = Hocon::ConfigValueFactory.from_any_ref(nil, nil)
31
+ expect(value).to be_instance_of(Hocon::Impl::ConfigNull)
32
+ expect(value.unwrapped).to be_nil
33
+ end
34
+
35
+ it "should convert an string into a ConfigString object" do
36
+ value = Hocon::ConfigValueFactory.from_any_ref("Hello, World!", nil)
37
+ expect(value).to be_a(Hocon::Impl::ConfigString)
38
+ expect(value.unwrapped).to eq("Hello, World!")
39
+ end
40
+
41
+ it "should convert an integer into a ConfigInt object" do
42
+ value = Hocon::ConfigValueFactory.from_any_ref(123, nil)
43
+ expect(value).to be_instance_of(Hocon::Impl::ConfigInt)
44
+ expect(value.unwrapped).to eq(123)
45
+ end
46
+
47
+ it "should convert a double into a ConfigDouble object" do
48
+ value = Hocon::ConfigValueFactory.from_any_ref(123.456, nil)
49
+ expect(value).to be_instance_of(Hocon::Impl::ConfigDouble)
50
+ expect(value.unwrapped).to eq(123.456)
51
+ end
52
+
53
+ it "should convert a map into a SimpleConfigObject" do
54
+ map = {"a" => 1, "b" => 2, "c" => 3}
55
+ value = Hocon::ConfigValueFactory.from_any_ref(map, nil)
56
+ expect(value).to be_instance_of(Hocon::Impl::SimpleConfigObject)
57
+ expect(value.unwrapped).to eq(map)
58
+ end
59
+
60
+ it "should convert symbol keys in a map to string keys" do
61
+ orig_map = {a: 1, b: 2, c: {a: 1, b: 2, c: {a: 1}}}
62
+ map = {"a" => 1, "b" => 2, "c"=>{"a"=>1, "b"=>2, "c"=>{"a"=>1}}}
63
+ value = Hocon::ConfigValueFactory.from_any_ref(orig_map, nil)
64
+ expect(value).to be_instance_of(Hocon::Impl::SimpleConfigObject)
65
+ expect(value.unwrapped).to eq(map)
66
+
67
+ value = Hocon::ConfigValueFactory.from_map(orig_map, nil)
68
+ expect(value).to be_instance_of(Hocon::Impl::SimpleConfigObject)
69
+ expect(value.unwrapped).to eq(map)
70
+ end
71
+
72
+ it "should not parse maps with non-string and non-symbol keys" do
73
+ map = {1 => "a", 2 => "b"}
74
+ expect{ Hocon::ConfigValueFactory.from_any_ref(map, nil) }.to raise_error(Hocon::ConfigError::ConfigBugOrBrokenError)
75
+ end
76
+
77
+ it "should convert an Enumerable into a SimpleConfigList" do
78
+ list = [1, 2, 3, 4, 5]
79
+ value = Hocon::ConfigValueFactory.from_any_ref(list, nil)
80
+ expect(value).to be_instance_of(Hocon::Impl::SimpleConfigList)
81
+ expect(value.unwrapped).to eq(list)
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,935 @@
1
+ require 'spec_helper'
2
+ require 'hocon'
3
+ require 'test_utils'
4
+
5
+ require 'hocon/impl/config_delayed_merge'
6
+ require 'hocon/impl/config_delayed_merge_object'
7
+ require 'hocon/config_error'
8
+ require 'hocon/impl/unsupported_operation_error'
9
+ require 'hocon/config_value_factory'
10
+ require 'hocon/config_render_options'
11
+
12
+
13
+
14
+ SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
15
+ SimpleConfigObject = Hocon::Impl::SimpleConfigObject
16
+ SimpleConfigList = Hocon::Impl::SimpleConfigList
17
+ SubstitutionExpression = Hocon::Impl::SubstitutionExpression
18
+ ConfigReference = Hocon::Impl::ConfigReference
19
+ ConfigConcatenation = Hocon::Impl::ConfigConcatenation
20
+ ConfigDelayedMerge = Hocon::Impl::ConfigDelayedMerge
21
+ ConfigDelayedMergeObject = Hocon::Impl::ConfigDelayedMergeObject
22
+ ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
23
+ UnresolvedSubstitutionError = Hocon::ConfigError::UnresolvedSubstitutionError
24
+ ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
25
+ AbstractConfigObject = Hocon::Impl::AbstractConfigObject
26
+ ConfigValueFactory = Hocon::ConfigValueFactory
27
+ ConfigFactory = Hocon::ConfigFactory
28
+ UnsupportedOperationError = Hocon::Impl::UnsupportedOperationError
29
+ ConfigNumber = Hocon::Impl::ConfigNumber
30
+ ConfigRenderOptions = Hocon::ConfigRenderOptions
31
+
32
+ describe "SimpleConfigOrigin equality" do
33
+ context "different origins with the same name should be equal" do
34
+ let(:a) { SimpleConfigOrigin.new_simple("foo") }
35
+ let(:same_as_a) { SimpleConfigOrigin.new_simple("foo") }
36
+ let(:b) { SimpleConfigOrigin.new_simple("bar") }
37
+
38
+ context "a equals a" do
39
+ let(:first_object) { a }
40
+ let(:second_object) { a }
41
+ include_examples "object_equality"
42
+ end
43
+
44
+ context "a equals same_as_a" do
45
+ let(:first_object) { a }
46
+ let(:second_object) { same_as_a }
47
+ include_examples "object_equality"
48
+ end
49
+
50
+ context "a does not equal b" do
51
+ let(:first_object) { a }
52
+ let(:second_object) { b }
53
+ include_examples "object_inequality"
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "ConfigInt equality" do
59
+ context "different ConfigInts with the same value should be equal" do
60
+ a = TestUtils.int_value(42)
61
+ same_as_a = TestUtils.int_value(42)
62
+ b = TestUtils.int_value(43)
63
+
64
+ context "a equals a" do
65
+ let(:first_object) { a }
66
+ let(:second_object) { a }
67
+ include_examples "object_equality"
68
+ end
69
+
70
+ context "a equals same_as_a" do
71
+ let(:first_object) { a }
72
+ let(:second_object) { same_as_a }
73
+ include_examples "object_equality"
74
+ end
75
+
76
+ context "a does not equal b" do
77
+ let(:first_object) { a }
78
+ let(:second_object) { b }
79
+ include_examples "object_inequality"
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "ConfigFloat equality" do
85
+ context "different ConfigFloats with the same value should be equal" do
86
+ a = TestUtils.double_value(3.14)
87
+ same_as_a = TestUtils.double_value(3.14)
88
+ b = TestUtils.double_value(4.14)
89
+
90
+ context "a equals a" do
91
+ let(:first_object) { a }
92
+ let(:second_object) { a }
93
+ include_examples "object_equality"
94
+ end
95
+
96
+ context "a equals same_as_a" do
97
+ let(:first_object) { a }
98
+ let(:second_object) { same_as_a }
99
+ include_examples "object_equality"
100
+ end
101
+
102
+ context "a does not equal b" do
103
+ let(:first_object) { a }
104
+ let(:second_object) { b }
105
+ include_examples "object_inequality"
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "ConfigFloat and ConfigInt equality" do
111
+ context "different ConfigInts with the same value should be equal" do
112
+ double_val = TestUtils.double_value(3.0)
113
+ int_value = TestUtils.int_value(3)
114
+ double_value_b = TestUtils.double_value(4.0)
115
+ int_value_b = TestUtils.double_value(4)
116
+
117
+ context "int equals double" do
118
+ let(:first_object) { double_val }
119
+ let(:second_object) { int_value }
120
+ include_examples "object_equality"
121
+ end
122
+
123
+ context "ConfigFloat made from int equals double" do
124
+ let(:first_object) { double_value_b }
125
+ let(:second_object) { int_value_b }
126
+ include_examples "object_equality"
127
+ end
128
+
129
+ context "3 doesn't equal 4.0" do
130
+ let(:first_object) { int_value }
131
+ let(:second_object) { double_value_b }
132
+ include_examples "object_inequality"
133
+ end
134
+
135
+ context "4.0 doesn't equal 3.0" do
136
+ let(:first_object) { int_value_b }
137
+ let(:second_object) { double_val }
138
+ include_examples "object_inequality"
139
+ end
140
+ end
141
+ end
142
+
143
+ describe "SimpleConfigObject equality" do
144
+ context "SimpleConfigObjects made from hash maps" do
145
+ a_map = TestUtils.config_map({a: 1, b: 2, c: 3})
146
+ same_as_a_map = TestUtils.config_map({a: 1, b: 2, c: 3})
147
+ b_map = TestUtils.config_map({a: 3, b: 4, c: 5})
148
+
149
+ # different keys is a different case in the equals implementation
150
+ c_map = TestUtils.config_map({x: 3, y: 4, z: 5})
151
+
152
+ a = SimpleConfigObject.new(TestUtils.fake_origin, a_map)
153
+ same_as_a = SimpleConfigObject.new(TestUtils.fake_origin, same_as_a_map)
154
+ b = SimpleConfigObject.new(TestUtils.fake_origin, b_map)
155
+ c = SimpleConfigObject.new(TestUtils.fake_origin, c_map)
156
+
157
+ # the config for an equal object is also equal
158
+ config = a.to_config
159
+
160
+ context "a equals a" do
161
+ let(:first_object) { a }
162
+ let(:second_object) { a }
163
+ include_examples "object_equality"
164
+ end
165
+
166
+ context "a equals same_as_a" do
167
+ let(:first_object) { a }
168
+ let(:second_object) { same_as_a }
169
+ include_examples "object_equality"
170
+ end
171
+
172
+ context "b equals b" do
173
+ let(:first_object) { b }
174
+ let(:second_object) { b }
175
+ include_examples "object_equality"
176
+ end
177
+
178
+ context "c equals c" do
179
+ let(:first_object) { c }
180
+ let(:second_object) { c }
181
+ include_examples "object_equality"
182
+ end
183
+
184
+ context "a doesn't equal b" do
185
+ let(:first_object) { a }
186
+ let(:second_object) { b }
187
+ include_examples "object_inequality"
188
+ end
189
+
190
+ context "a doesn't equal c" do
191
+ let(:first_object) { a }
192
+ let(:second_object) { c }
193
+ include_examples "object_inequality"
194
+ end
195
+
196
+ context "b doesn't equal c" do
197
+ let(:first_object) { b }
198
+ let(:second_object) { c }
199
+ include_examples "object_inequality"
200
+ end
201
+
202
+ context "a's config equals a's config" do
203
+ let(:first_object) { config }
204
+ let(:second_object) { config }
205
+ include_examples "object_equality"
206
+ end
207
+
208
+ context "a's config equals same_as_a's config" do
209
+ let(:first_object) { config }
210
+ let(:second_object) { same_as_a.to_config }
211
+ include_examples "object_equality"
212
+ end
213
+
214
+ context "a's config equals a's config computed again" do
215
+ let(:first_object) { config }
216
+ let(:second_object) { a.to_config }
217
+ include_examples "object_equality"
218
+ end
219
+
220
+ context "a's config doesn't equal b's config" do
221
+ let(:first_object) { config }
222
+ let(:second_object) { b.to_config }
223
+ include_examples "object_inequality"
224
+ end
225
+
226
+ context "a's config doesn't equal c's config" do
227
+ let(:first_object) { config }
228
+ let(:second_object) { c.to_config }
229
+ include_examples "object_inequality"
230
+ end
231
+
232
+ context "a doesn't equal a's config" do
233
+ let(:first_object) { a }
234
+ let(:second_object) { config }
235
+ include_examples "object_inequality"
236
+ end
237
+
238
+ context "b doesn't equal b's config" do
239
+ let(:first_object) { b }
240
+ let(:second_object) { b.to_config }
241
+ include_examples "object_inequality"
242
+ end
243
+ end
244
+ end
245
+
246
+ describe "SimpleConfigList equality" do
247
+ a_values = [1, 2, 3].map { |i| TestUtils.int_value(i) }
248
+ a_list = SimpleConfigList.new(TestUtils.fake_origin, a_values)
249
+
250
+ same_as_a_values = [1, 2, 3].map { |i| TestUtils.int_value(i) }
251
+ same_as_a_list = SimpleConfigList.new(TestUtils.fake_origin, same_as_a_values)
252
+
253
+ b_values = [4, 5, 6].map { |i| TestUtils.int_value(i) }
254
+ b_list = SimpleConfigList.new(TestUtils.fake_origin, b_values)
255
+
256
+ context "a_list equals a_list" do
257
+ let(:first_object) { a_list }
258
+ let(:second_object) { a_list }
259
+ include_examples "object_equality"
260
+ end
261
+
262
+ context "a_list equals same_as_a_list" do
263
+ let(:first_object) { a_list }
264
+ let(:second_object) { same_as_a_list }
265
+ include_examples "object_equality"
266
+ end
267
+
268
+ context "a_list doesn't equal b_list" do
269
+ let(:first_object) { a_list }
270
+ let(:second_object) { b_list }
271
+ include_examples "object_inequality"
272
+ end
273
+ end
274
+
275
+ describe "ConfigReference equality" do
276
+ a = TestUtils.subst("foo")
277
+ same_as_a = TestUtils.subst("foo")
278
+ b = TestUtils.subst("bar")
279
+ c = TestUtils.subst("foo", true)
280
+
281
+ specify "testing values are of the right type" do
282
+ expect(a).to be_instance_of(ConfigReference)
283
+ expect(b).to be_instance_of(ConfigReference)
284
+ expect(c).to be_instance_of(ConfigReference)
285
+ end
286
+
287
+ context "a equals a" do
288
+ let(:first_object) { a }
289
+ let(:second_object) { a }
290
+ include_examples "object_equality"
291
+ end
292
+
293
+ context "a equals same_as_a" do
294
+ let(:first_object) { a }
295
+ let(:second_object) { same_as_a }
296
+ include_examples "object_equality"
297
+ end
298
+
299
+ context "a doesn't equal b" do
300
+ let(:first_object) { a }
301
+ let(:second_object) { b }
302
+ include_examples "object_inequality"
303
+ end
304
+
305
+ context "a doesn't equal c, an optional substitution" do
306
+ let(:first_object) { a }
307
+ let(:second_object) { c }
308
+ include_examples "object_inequality"
309
+ end
310
+ end
311
+
312
+ describe "ConfigConcatenation equality" do
313
+ a = TestUtils.subst_in_string("foo")
314
+ same_as_a = TestUtils.subst_in_string("foo")
315
+ b = TestUtils.subst_in_string("bar")
316
+ c = TestUtils.subst_in_string("foo", true)
317
+
318
+ specify "testing values are of the right type" do
319
+ expect(a).to be_instance_of(ConfigConcatenation)
320
+ expect(b).to be_instance_of(ConfigConcatenation)
321
+ expect(c).to be_instance_of(ConfigConcatenation)
322
+ end
323
+
324
+ context "a equals a" do
325
+ let(:first_object) { a }
326
+ let(:second_object) { a }
327
+ include_examples "object_equality"
328
+ end
329
+
330
+ context "a equals same_as_a" do
331
+ let(:first_object) { a }
332
+ let(:second_object) { same_as_a }
333
+ include_examples "object_equality"
334
+ end
335
+
336
+ context "a doesn't equal b" do
337
+ let(:first_object) { a }
338
+ let(:second_object) { b }
339
+ include_examples "object_inequality"
340
+ end
341
+
342
+ context "a doesn't equal c, an optional substitution" do
343
+ let(:first_object) { a }
344
+ let(:second_object) { c }
345
+ include_examples "object_inequality"
346
+ end
347
+ end
348
+
349
+ describe "ConfigDelayedMerge equality" do
350
+ s1 = TestUtils.subst("foo")
351
+ s2 = TestUtils.subst("bar")
352
+ a = ConfigDelayedMerge.new(TestUtils.fake_origin, [s1, s2])
353
+ same_as_a = ConfigDelayedMerge.new(TestUtils.fake_origin, [s1, s2])
354
+ b = ConfigDelayedMerge.new(TestUtils.fake_origin, [s2, s1])
355
+
356
+ context "a equals a" do
357
+ let(:first_object) { a }
358
+ let(:second_object) { a }
359
+ include_examples "object_equality"
360
+ end
361
+
362
+ context "a equals same_as_a" do
363
+ let(:first_object) { a }
364
+ let(:second_object) { same_as_a }
365
+ include_examples "object_equality"
366
+ end
367
+
368
+ context "a doesn't equal b" do
369
+ let(:first_object) { a }
370
+ let(:second_object) { b }
371
+ include_examples "object_inequality"
372
+ end
373
+ end
374
+
375
+ describe "ConfigDelayedMergeObject equality" do
376
+ empty = SimpleConfigObject.empty
377
+ s1 = TestUtils.subst("foo")
378
+ s2 = TestUtils.subst("bar")
379
+ a = ConfigDelayedMergeObject.new(TestUtils.fake_origin, [empty, s1, s2])
380
+ same_as_a = ConfigDelayedMergeObject.new(TestUtils.fake_origin, [empty, s1, s2])
381
+ b = ConfigDelayedMergeObject.new(TestUtils.fake_origin, [empty, s2, s1])
382
+
383
+ context "a equals a" do
384
+ let(:first_object) { a }
385
+ let(:second_object) { a }
386
+ include_examples "object_equality"
387
+ end
388
+
389
+ context "a equals same_as_a" do
390
+ let(:first_object) { a }
391
+ let(:second_object) { same_as_a }
392
+ include_examples "object_equality"
393
+ end
394
+
395
+ context "a doesn't equal b" do
396
+ let(:first_object) { a }
397
+ let(:second_object) { b }
398
+ include_examples "object_inequality"
399
+ end
400
+ end
401
+
402
+ describe "Values' to_s methods" do
403
+ # just check that these don't throw, the exact output
404
+ # isn't super important since it's just for debugging
405
+
406
+ specify "to_s doesn't throw error" do
407
+ TestUtils.int_value(10).to_s
408
+ TestUtils.double_value(3.14).to_s
409
+ TestUtils.string_value("hi").to_s
410
+ TestUtils.null_value.to_s
411
+ TestUtils.bool_value(true).to_s
412
+ empty_object = SimpleConfigObject.empty
413
+ empty_object.to_s
414
+
415
+ SimpleConfigList.new(TestUtils.fake_origin, []).to_s
416
+ TestUtils.subst("a").to_s
417
+ TestUtils.subst_in_string("b").to_s
418
+ dm = ConfigDelayedMerge.new(TestUtils.fake_origin, [TestUtils.subst("a"), TestUtils.subst("b")])
419
+ dm.to_s
420
+
421
+ dmo = ConfigDelayedMergeObject.new(TestUtils.fake_origin, [empty_object, TestUtils.subst("a"), TestUtils.subst("b")])
422
+ dmo.to_s
423
+
424
+ TestUtils.fake_origin.to_s
425
+ end
426
+ end
427
+
428
+ describe "ConfigObject" do
429
+ specify "should unwrap correctly" do
430
+ m = SimpleConfigObject.new(TestUtils.fake_origin, TestUtils.config_map({a: 1, b: 2, c: 3}))
431
+
432
+ expect({a: 1, b: 2, c: 3}).to eq(m.unwrapped)
433
+ end
434
+
435
+ specify "should implement read only map" do
436
+ m = SimpleConfigObject.new(TestUtils.fake_origin, TestUtils.config_map({a: 1, b: 2, c: 3}))
437
+
438
+ expect(TestUtils.int_value(1)).to eq(m[:a])
439
+ expect(TestUtils.int_value(2)).to eq(m[:b])
440
+ expect(TestUtils.int_value(3)).to eq(m[:c])
441
+ expect(m[:d]).to be_nil
442
+ # [] can take a non-string
443
+ expect(m[[]]).to be_nil
444
+
445
+ expect(m.has_key? :a).to be_truthy
446
+ expect(m.has_key? :z).to be_falsey
447
+ # has_key? can take a non-string
448
+ expect(m.has_key? []).to be_falsey
449
+
450
+ expect(m.has_value? TestUtils.int_value(1)).to be_truthy
451
+ expect(m.has_value? TestUtils.int_value(10)).to be_falsey
452
+ # has_value? can take a non-string
453
+ expect(m.has_value? []).to be_falsey
454
+
455
+ expect(m.empty?).to be_falsey
456
+
457
+ expect(m.size).to eq(3)
458
+
459
+ values = [TestUtils.int_value(1), TestUtils.int_value(2), TestUtils.int_value(3)]
460
+ expect(values).to eq(m.values)
461
+
462
+ keys = [:a, :b, :c]
463
+ expect(keys).to eq(m.keys)
464
+
465
+ expect { m["hello"] = TestUtils.int_value(41) }.to raise_error(UnsupportedOperationError)
466
+ expect { m.delete(:a) }.to raise_error(UnsupportedOperationError)
467
+ end
468
+ end
469
+
470
+ describe "ConfigList" do
471
+ specify "should implement read only list" do
472
+ values = ["a", "b", "c"].map { |i| TestUtils.string_value(i) }
473
+ l = SimpleConfigList.new(TestUtils.fake_origin, values)
474
+
475
+ expect(values[0]).to eq(l[0])
476
+ expect(values[1]).to eq(l[1])
477
+ expect(values[2]).to eq(l[2])
478
+
479
+ expect(l.include? TestUtils.string_value("a")).to be_truthy
480
+ expect(l.include_all?([TestUtils.string_value("a")])).to be_truthy
481
+ expect(l.include_all?([TestUtils.string_value("b")])).to be_truthy
482
+ expect(l.include_all?(values)).to be_truthy
483
+
484
+ expect(l.index(values[1])).to eq(1)
485
+
486
+ expect(l.empty?).to be_falsey
487
+
488
+ expect(l.map { |v| v }).to eq(values.map { |v| v })
489
+
490
+ expect(l.rindex(values[1])).to eq(1)
491
+
492
+ expect(l.size).to eq(3)
493
+
494
+ expect { l.push(TestUtils.int_value(3)) }.to raise_error(UnsupportedOperationError)
495
+ expect { l << TestUtils.int_value(3) }.to raise_error(UnsupportedOperationError)
496
+ expect { l.clear }.to raise_error(UnsupportedOperationError)
497
+ expect { l.delete(TestUtils.int_value(2)) }.to raise_error(UnsupportedOperationError)
498
+ expect { l.delete(1) }.to raise_error(UnsupportedOperationError)
499
+ expect { l[0] = TestUtils.int_value(42) }.to raise_error(UnsupportedOperationError)
500
+ end
501
+ end
502
+
503
+ describe "Objects throwing ConfigNotResolvedError" do
504
+ context "ConfigSubstitution" do
505
+ specify "should throw ConfigNotResolvedError" do
506
+ expect{ TestUtils.subst("foo").value_type }.to raise_error(ConfigNotResolvedError)
507
+ expect{ TestUtils.subst("foo").unwrapped }.to raise_error(ConfigNotResolvedError)
508
+ end
509
+ end
510
+
511
+ context "ConfigDelayedMerge" do
512
+ let(:dm) { ConfigDelayedMerge.new(TestUtils.fake_origin, [TestUtils.subst("a"), TestUtils.subst("b")]) }
513
+
514
+ specify "should throw ConfigNotResolvedError" do
515
+ expect{ dm.value_type }.to raise_error(ConfigNotResolvedError)
516
+ expect{ dm.unwrapped }.to raise_error(ConfigNotResolvedError)
517
+ end
518
+ end
519
+
520
+ context "ConfigDelayedMergeObject" do
521
+ empty_object = SimpleConfigObject.empty
522
+ objects = [empty_object, TestUtils.subst("a"), TestUtils.subst("b")]
523
+
524
+ let(:dmo) { ConfigDelayedMergeObject.new(TestUtils.fake_origin, objects) }
525
+
526
+ specify "should have value type of OBJECT" do
527
+ expect(dmo.value_type).to eq(Hocon::ConfigValueType::OBJECT)
528
+ end
529
+
530
+ specify "should throw ConfigNotResolvedError" do
531
+ expect{ dmo.unwrapped }.to raise_error(ConfigNotResolvedError)
532
+ expect{ dmo["foo"] }.to raise_error(ConfigNotResolvedError)
533
+ expect{ dmo.has_key?(nil) }.to raise_error(ConfigNotResolvedError)
534
+ expect{ dmo.has_value?(nil) }.to raise_error(ConfigNotResolvedError)
535
+ expect{ dmo.each }.to raise_error(ConfigNotResolvedError)
536
+ expect{ dmo.empty? }.to raise_error(ConfigNotResolvedError)
537
+ expect{ dmo.keys }.to raise_error(ConfigNotResolvedError)
538
+ expect{ dmo.size }.to raise_error(ConfigNotResolvedError)
539
+ expect{ dmo.values }.to raise_error(ConfigNotResolvedError)
540
+ expect{ dmo.to_config.get_int("foo") }.to raise_error(ConfigNotResolvedError)
541
+ end
542
+ end
543
+ end
544
+
545
+ describe "Round tripping numbers through parse_string" do
546
+ specify "should get the same numbers back out" do
547
+ # formats rounded off with E notation
548
+ a = "132454454354353245.3254652656454808909932874873298473298472"
549
+ # formats as 100000.0
550
+ b = "1e6"
551
+ # formats as 5.0E-5
552
+ c = "0.00005"
553
+ # formats as 1E100 (capital E)
554
+ d = "1e100"
555
+
556
+ object = TestUtils.parse_config("{ a : #{a}, b : #{b}, c : #{c}, d : #{d}}")
557
+ expect([a, b, c, d]).to eq(["a", "b", "c", "d"].map { |x| object.get_string(x) })
558
+
559
+ object2 = TestUtils.parse_config("{ a : xx #{a} yy, b : xx #{b} yy, c : xx #{c} yy, d : xx #{d} yy}")
560
+ expected2 = [a, b, c, d].map { |x| "xx #{x} yy"}
561
+ expect(["a", "b", "c", "d"].map { |x| object2.get_string(x) }).to eq(expected2)
562
+ end
563
+ end
564
+
565
+
566
+
567
+ describe "AbstractConfigObject#merge_origins" do
568
+ def o(desc, empty)
569
+ values = {}
570
+
571
+ if !empty
572
+ values["hello"] = TestUtils.int_value(37)
573
+ end
574
+
575
+ SimpleConfigObject.new(SimpleConfigOrigin.new_simple(desc), values)
576
+ end
577
+
578
+ def m(*values)
579
+ AbstractConfigObject.merge_origins(values).description
580
+ end
581
+
582
+ specify "should merge origins correctly" do
583
+ # simplest case
584
+ expect(m(o("a", false), o("b", false))).to eq("merge of a,b")
585
+
586
+ # combine duplicate "merge of"
587
+ expect(m(o("a", false), o("merge of x,y", false))).to eq("merge of a,x,y")
588
+ expect(m(o("merge of a,b", false), o("merge of x,y", false))).to eq("merge of a,b,x,y")
589
+ # ignore empty objects
590
+ expect(m(o("foo", true), o("a", false))).to eq("a")
591
+ # unless they are all empty, pick the first one
592
+ expect(m(o("foo", true), o("a", true))).to eq("foo")
593
+ # merge just one
594
+ expect(m(o("foo", false))).to eq("foo")
595
+ # merge three
596
+ expect(m(o("a", false), o("b", false), o("c", false))).to eq("merge of a,b,c")
597
+ end
598
+ end
599
+
600
+ describe "SimpleConfig#has_path?" do
601
+ specify "should work in various contexts" do
602
+ empty = TestUtils.parse_config("{}")
603
+
604
+ expect(empty.has_path?("foo")).to be_falsey
605
+
606
+ object = TestUtils.parse_config("a=null, b.c.d=11, foo=bar")
607
+
608
+ # returns true for the non-null values
609
+ expect(object.has_path?("foo")).to be_truthy
610
+ expect(object.has_path?("b.c.d")).to be_truthy
611
+ expect(object.has_path?("b.c")).to be_truthy
612
+ expect(object.has_path?("b")).to be_truthy
613
+
614
+ # has_path is false for null values but contains_key is true
615
+ expect(object.root["a"]).to eq(TestUtils.null_value)
616
+ expect(object.root.has_key?("a")).to be_truthy
617
+ expect(object.has_path?("a")).to be_falsey
618
+
619
+ # false for totally absent values
620
+ expect(object.root.has_key?("notinhere")).to be_falsey
621
+ expect(object.has_path?("notinhere")).to be_falsey
622
+
623
+ # throws proper exceptions
624
+ expect { empty.has_path?("a.") }.to raise_error(Hocon::ConfigError::ConfigBadPathError)
625
+ expect { empty.has_path?("..") }.to raise_error(Hocon::ConfigError::ConfigBadPathError)
626
+ end
627
+ end
628
+
629
+ describe "ConfigNumber::new_number" do
630
+ specify "should create new objects correctly" do
631
+ def n(v)
632
+ ConfigNumber.new_number(TestUtils.fake_origin, v, nil)
633
+ end
634
+
635
+ expect(n(3.14).unwrapped).to eq(3.14)
636
+ expect(n(1).unwrapped).to eq(1)
637
+ expect(n(1).unwrapped).to eq(1.0)
638
+ end
639
+ end
640
+
641
+ describe "Boolean conversions" do
642
+ specify "true, yes, and on all convert to true" do
643
+ trues = TestUtils.parse_object("{ a=true, b=yes, c=on }").to_config
644
+ ["a", "b", "c"].map { |x| expect(trues.get_boolean(x)).to be true }
645
+
646
+ falses = TestUtils.parse_object("{ a=false, b=no, c=off }").to_config
647
+ ["a", "b", "c"].map { |x| expect(falses.get_boolean(x)).to be false }
648
+ end
649
+ end
650
+
651
+ describe "SimpleConfigOrigin" do
652
+ let(:has_filename) { SimpleConfigOrigin.new_file("foo") }
653
+ let(:no_filename) { SimpleConfigOrigin.new_simple("bar") }
654
+ let(:filename_with_line) { has_filename.with_line_number(3) }
655
+ let(:no_filename_with_line) { no_filename.with_line_number(4) }
656
+
657
+ specify "filename matches what was specified" do
658
+ expect(has_filename.filename).to eq("foo")
659
+ expect(filename_with_line.filename).to eq("foo")
660
+ expect(no_filename.filename).to be nil
661
+ expect(no_filename_with_line.filename).to be nil
662
+ end
663
+
664
+ specify "description matches correctly" do
665
+ expect(has_filename.description).to eq("foo")
666
+ expect(no_filename.description).to eq("bar")
667
+ expect(filename_with_line.description).to eq("foo: 3")
668
+ expect(no_filename_with_line.description).to eq("bar: 4")
669
+ end
670
+
671
+ specify "origins with no line number should have line number of -1" do
672
+ expect(has_filename.line_number).to eq(-1)
673
+ expect(no_filename.line_number).to eq(-1)
674
+ end
675
+
676
+ specify "line_number returns the right line number" do
677
+ expect(filename_with_line.line_number).to eq(3)
678
+ expect(no_filename_with_line.line_number).to eq(4)
679
+ end
680
+
681
+ # Note: skipping tests related to URLs since we aren't implementing that
682
+ end
683
+
684
+
685
+ describe "Config#with_only_key and with_only_path" do
686
+ context "should keep the correct data" do
687
+ object = TestUtils.parse_object("{ a=1, b=2, c.d.y=3, e.f.g=4, c.d.z=5 }")
688
+
689
+ it "should keep only a" do
690
+ expect(object.with_only_key("a")).to eq(TestUtils.parse_object("{ a=1 }"))
691
+ end
692
+
693
+ it "should keep only e" do
694
+ expect(object.with_only_key("e")).to eq(TestUtils.parse_object("{ e.f.g=4 }"))
695
+ end
696
+
697
+ it "should keep only c.d" do
698
+ expect(object.to_config.with_only_path("c.d").root).to eq(TestUtils.parse_object("{ c.d.y=3, c.d.z=5 }"))
699
+ end
700
+
701
+ it "should keep only c.d.z" do
702
+ expect(object.to_config.with_only_path("c.d.z").root).to eq(TestUtils.parse_object("{ c.d.z=5 }"))
703
+ end
704
+
705
+ it "should keep nonexistent key" do
706
+ expect(object.with_only_key("nope")).to eq(TestUtils.parse_object("{ }"))
707
+ end
708
+
709
+ it "should keep nonexistent path" do
710
+ expect(object.to_config.with_only_path("q.w.e.r.t.y").root).to eq(TestUtils.parse_object("{ }"))
711
+ end
712
+
713
+ it "should keep only nonexistent underneath non-object" do
714
+ expect(object.to_config.with_only_path("a.nonextistent").root).to eq(TestUtils.parse_object("{ }"))
715
+ end
716
+
717
+ it "should keep only nonexistent underneath nested non-object" do
718
+ expect(object.to_config.with_only_path("c.d.z.nonexistent").root).to eq(TestUtils.parse_object("{ }"))
719
+ end
720
+ end
721
+
722
+ specify "should handle unresolved correctly" do
723
+ object = TestUtils.parse_object("{ a = {}, a=${x}, b=${y}, b=${z}, x={asf:1}, y=2, z=3 }")
724
+
725
+ expect(object.to_config.resolve.with_only_path("a.asf").root).to eq(TestUtils.parse_object("{ a={asf:1} }"))
726
+
727
+ TestUtils.intercept(UnresolvedSubstitutionError) do
728
+ object.with_only_key("a").to_config.resolve
729
+ end
730
+
731
+ TestUtils.intercept(UnresolvedSubstitutionError) do
732
+ object.with_only_key("b").to_config.resolve
733
+ end
734
+
735
+ expect(object.resolve_status).to eq(Hocon::Impl::ResolveStatus::UNRESOLVED)
736
+ expect(object.with_only_key("z").resolve_status).to eq(Hocon::Impl::ResolveStatus::RESOLVED)
737
+ end
738
+ end
739
+
740
+ describe "Config#without_key/path" do
741
+
742
+ context "should remove keys correctly" do
743
+ object = TestUtils.parse_object("{ a=1, b=2, c.d.y=3, e.f.g=4, c.d.z=5 }")
744
+
745
+ it "should not have a" do
746
+ expect(object.without_key("a")).to eq(TestUtils.parse_object("{ b=2, c.d.y=3, e.f.g=4, c.d.z=5 }"))
747
+ end
748
+
749
+ it "should not have c" do
750
+ expect(object.without_key("c")).to eq(TestUtils.parse_object("{ a=1, b=2, e.f.g=4 }"))
751
+ end
752
+
753
+ it "should not have c.d" do
754
+ expect(object.to_config.without_path("c.d").root).to eq(TestUtils.parse_object("{ a=1, b=2, e.f.g=4, c={} }"))
755
+ end
756
+
757
+ it "should not have c.d.z" do
758
+ expect(object.to_config.without_path("c.d.z").root).to eq(TestUtils.parse_object("{ a=1, b=2, c.d.y=3, e.f.g=4 }"))
759
+ end
760
+
761
+ it "should not change without nonexistent key" do
762
+ expect(object.without_key("nonexistent")).to eq(TestUtils.parse_object("{ a=1, b=2, c.d.y=3, e.f.g=4, c.d.z=5 }"))
763
+ end
764
+
765
+ it "should not change without nonexistent path" do
766
+ expect(object.to_config.without_path("q.w.e.r.t.y").root).to eq(TestUtils.parse_object("{ a=1, b=2, c.d.y=3, e.f.g=4, c.d.z=5 }"))
767
+ end
768
+
769
+ it "should not change without nonexistent path with existing prefix" do
770
+ expect(object.to_config.without_path("a.foo").root).to eq(TestUtils.parse_object("{ a=1, b=2, c.d.y=3, e.f.g=4, c.d.z=5 }"))
771
+ end
772
+ end
773
+ end
774
+
775
+ describe "Config#without_key/path involving unresolved" do
776
+
777
+ specify "should handle unresolved correctly" do
778
+ object = TestUtils.parse_object("{ a = {}, a=${x}, b=${y}, b=${z}, x={asf:1}, y=2, z=3 }")
779
+
780
+ expect(object.to_config.resolve.without_path("a.asf").root).to eq(TestUtils.parse_object("{ a={}, b=3, x={asf:1}, y=2, z=3 }"))
781
+
782
+ TestUtils.intercept(UnresolvedSubstitutionError) do
783
+ object.without_key("x").to_config.resolve
784
+ end
785
+
786
+ TestUtils.intercept(UnresolvedSubstitutionError) do
787
+ object.without_key("z").to_config.resolve
788
+ end
789
+
790
+ expect(object.resolve_status).to eq(Hocon::Impl::ResolveStatus::UNRESOLVED)
791
+ expect(object.without_key("a").resolve_status).to eq(Hocon::Impl::ResolveStatus::UNRESOLVED)
792
+ expect(object.without_key("a").without_key("b").resolve_status).to eq(Hocon::Impl::ResolveStatus::RESOLVED)
793
+ end
794
+ end
795
+
796
+ describe "Config#at_path" do
797
+ specify "works with one element" do
798
+ v = ConfigValueFactory.from_any_ref(42)
799
+ config = v.at_path("a")
800
+
801
+ expect(config).to eq(TestUtils.parse_config("a=42"))
802
+ expect(v).to eq(config.get_value("a"))
803
+ expect(config.origin.description).to include("at_path")
804
+ end
805
+
806
+ specify "works with two elements" do
807
+ v = ConfigValueFactory.from_any_ref(42)
808
+ config = v.at_path("a.b")
809
+
810
+ expect(config).to eq(TestUtils.parse_config("a.b=42"))
811
+ expect(v).to eq(config.get_value("a.b"))
812
+ expect(config.origin.description).to include("at_path")
813
+ end
814
+
815
+ specify "works with four elements" do
816
+ v = ConfigValueFactory.from_any_ref(42)
817
+ config = v.at_path("a.b.c.d")
818
+
819
+ expect(config).to eq(TestUtils.parse_config("a.b.c.d=42"))
820
+ expect(v).to eq(config.get_value("a.b.c.d"))
821
+ expect(config.origin.description).to include("at_path")
822
+ end
823
+ end
824
+
825
+ describe "Config#at_key" do
826
+ specify "at_key works" do
827
+ v = ConfigValueFactory.from_any_ref(42)
828
+ config = v.at_key("a")
829
+
830
+ expect(config).to eq(TestUtils.parse_config("a=42"))
831
+ expect(v).to eq(config.get_value("a"))
832
+ expect(config.origin.description).to include("at_key")
833
+ end
834
+
835
+ specify "works with value depth 1 from empty" do
836
+ v = ConfigValueFactory.from_any_ref(42)
837
+ config = ConfigFactory.empty.with_value("a", v)
838
+
839
+ expect(config).to eq(TestUtils.parse_config("a=42"))
840
+ expect(v).to eq(config.get_value("a"))
841
+ end
842
+
843
+ specify "works with value depth 2 from empty" do
844
+ v = ConfigValueFactory.from_any_ref(42)
845
+ config = ConfigFactory.empty.with_value("a.b", v)
846
+
847
+ expect(config).to eq(TestUtils.parse_config("a.b=42"))
848
+ expect(v).to eq(config.get_value("a.b"))
849
+ end
850
+
851
+ specify "works with value depth 3 from empty" do
852
+ v = ConfigValueFactory.from_any_ref(42)
853
+ config = ConfigFactory.empty.with_value("a.b.c", v)
854
+
855
+ expect(config).to eq(TestUtils.parse_config("a.b.c=42"))
856
+ expect(v).to eq(config.get_value("a.b.c"))
857
+ end
858
+
859
+ specify "with value depth 1 overwrites existing" do
860
+ v = ConfigValueFactory.from_any_ref(47)
861
+ old = v.at_path("a")
862
+ config = old.with_value("a", ConfigValueFactory.from_any_ref(42))
863
+
864
+ expect(config).to eq(TestUtils.parse_config("a=42"))
865
+ expect(config.get_int("a")).to eq(42)
866
+ end
867
+
868
+ specify "with value depth 2 overwrites existing" do
869
+ v = ConfigValueFactory.from_any_ref(47)
870
+ old = v.at_path("a.b")
871
+ config = old.with_value("a.b", ConfigValueFactory.from_any_ref(42))
872
+
873
+ expect(config).to eq(TestUtils.parse_config("a.b=42"))
874
+ expect(config.get_int("a.b")).to eq(42)
875
+ end
876
+
877
+ specify "with value inside existing object" do
878
+ v = ConfigValueFactory.from_any_ref(47)
879
+ old = v.at_path("a.c")
880
+ config = old.with_value("a.b", ConfigValueFactory.from_any_ref(42))
881
+
882
+ expect(config).to eq(TestUtils.parse_config("a.b=42,a.c=47"))
883
+ expect(config.get_int("a.b")).to eq(42)
884
+ expect(config.get_int("a.c")).to eq(47)
885
+ end
886
+
887
+ specify "with value build complex config" do
888
+ v1 = ConfigValueFactory.from_any_ref(1)
889
+ v2 = ConfigValueFactory.from_any_ref(2)
890
+ v3 = ConfigValueFactory.from_any_ref(3)
891
+ v4 = ConfigValueFactory.from_any_ref(4)
892
+
893
+ config = ConfigFactory.empty.with_value("a", v1)
894
+ .with_value("b.c", v2)
895
+ .with_value("b.d", v3)
896
+ .with_value("x.y.z", v4)
897
+
898
+ expect(config).to eq(TestUtils.parse_config("a=1,b.c=2,b.d=3,x.y.z=4"))
899
+ end
900
+ end
901
+
902
+ describe "#render" do
903
+ context "has newlines in description" do
904
+ v = ConfigValueFactory.from_any_ref(89, "this is a description\nwith some\nnewlines")
905
+
906
+ list = SimpleConfigList.new(SimpleConfigOrigin.new_simple("\n5\n6\n7\n"), [v])
907
+
908
+ conf = ConfigFactory.empty.with_value("bar", list)
909
+
910
+ rendered = conf.root.render
911
+
912
+ specify "rendered config should have all the lines that were added, with newlines" do
913
+ expect(rendered).to include("is a description\n")
914
+ expect(rendered).to include("with some\n")
915
+ expect(rendered).to include("newlines\n")
916
+ expect(rendered).to include("#\n")
917
+ expect(rendered).to include("5\n")
918
+ expect(rendered).to include("6\n")
919
+ expect(rendered).to include("7\n")
920
+ end
921
+
922
+ specify "the rendered config should give back the original config" do
923
+ parsed = ConfigFactory.parse_string(rendered)
924
+
925
+ expect(parsed).to eq(conf)
926
+ end
927
+ end
928
+
929
+ specify "should sort properly" do
930
+ config = TestUtils.parse_config('0=a,1=b,2=c,3=d,10=e,20=f,30=g')
931
+ rendered = config.root.render(ConfigRenderOptions.concise)
932
+
933
+ expect(rendered).to eq('{"0":"a","1":"b","2":"c","3":"d","10":"e","20":"f","30":"g"}')
934
+ end
935
+ end