hocon 0.9.5 → 1.0.1

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 (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