hocon 1.1.3 → 1.3.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 (62) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +35 -1
  3. data/HISTORY.md +2140 -0
  4. data/README.md +125 -0
  5. data/bin/hocon +5 -0
  6. data/lib/hocon/cli.rb +225 -0
  7. data/lib/hocon/config_render_options.rb +3 -2
  8. data/lib/hocon/impl/abstract_config_value.rb +7 -10
  9. data/lib/hocon/impl/config_delayed_merge.rb +1 -1
  10. data/lib/hocon/impl/config_impl.rb +18 -0
  11. data/lib/hocon/impl/config_node_object.rb +1 -2
  12. data/lib/hocon/impl/config_node_root.rb +1 -1
  13. data/lib/hocon/impl/parseable.rb +6 -5
  14. data/lib/hocon/impl/simple_config_document.rb +1 -1
  15. data/lib/hocon/impl/simple_config_origin.rb +8 -2
  16. data/lib/hocon/version.rb +5 -0
  17. metadata +9 -50
  18. data/spec/fixtures/hocon/by_extension/cat.conf +0 -4
  19. data/spec/fixtures/hocon/by_extension/cat.test +0 -4
  20. data/spec/fixtures/hocon/by_extension/cat.test-json +0 -3
  21. data/spec/fixtures/hocon/with_substitution/subst.conf +0 -2
  22. data/spec/fixtures/parse_render/example1/input.conf +0 -21
  23. data/spec/fixtures/parse_render/example1/output.conf +0 -26
  24. data/spec/fixtures/parse_render/example1/output_nocomments.conf +0 -17
  25. data/spec/fixtures/parse_render/example2/input.conf +0 -10
  26. data/spec/fixtures/parse_render/example2/output.conf +0 -17
  27. data/spec/fixtures/parse_render/example2/output_nocomments.conf +0 -17
  28. data/spec/fixtures/parse_render/example3/input.conf +0 -2
  29. data/spec/fixtures/parse_render/example3/output.conf +0 -2
  30. data/spec/fixtures/parse_render/example4/input.json +0 -6
  31. data/spec/fixtures/parse_render/example4/output.conf +0 -6
  32. data/spec/fixtures/test_utils/resources/bom.conf +0 -2
  33. data/spec/fixtures/test_utils/resources/cycle.conf +0 -1
  34. data/spec/fixtures/test_utils/resources/file-include.conf +0 -5
  35. data/spec/fixtures/test_utils/resources/include-from-list.conf +0 -4
  36. data/spec/fixtures/test_utils/resources/subdir/bar.conf +0 -1
  37. data/spec/fixtures/test_utils/resources/subdir/baz.conf +0 -1
  38. data/spec/fixtures/test_utils/resources/subdir/foo.conf +0 -5
  39. data/spec/fixtures/test_utils/resources/test01.conf +0 -80
  40. data/spec/fixtures/test_utils/resources/test01.json +0 -4
  41. data/spec/fixtures/test_utils/resources/test03.conf +0 -36
  42. data/spec/fixtures/test_utils/resources/utf16.conf +0 -0
  43. data/spec/fixtures/test_utils/resources/utf8.conf +0 -2
  44. data/spec/fixtures/test_utils/resources//341/232/240/341/233/207/341/232/273.conf +0 -2
  45. data/spec/spec_helper.rb +0 -43
  46. data/spec/test_utils.rb +0 -757
  47. data/spec/unit/hocon/README.md +0 -7
  48. data/spec/unit/hocon/hocon_spec.rb +0 -114
  49. data/spec/unit/typesafe/config/README.md +0 -4
  50. data/spec/unit/typesafe/config/concatenation_spec.rb +0 -417
  51. data/spec/unit/typesafe/config/conf_parser_spec.rb +0 -832
  52. data/spec/unit/typesafe/config/config_document_parser_spec.rb +0 -494
  53. data/spec/unit/typesafe/config/config_document_spec.rb +0 -576
  54. data/spec/unit/typesafe/config/config_factory_spec.rb +0 -120
  55. data/spec/unit/typesafe/config/config_node_spec.rb +0 -552
  56. data/spec/unit/typesafe/config/config_value_factory_spec.rb +0 -85
  57. data/spec/unit/typesafe/config/config_value_spec.rb +0 -935
  58. data/spec/unit/typesafe/config/path_spec.rb +0 -261
  59. data/spec/unit/typesafe/config/public_api_spec.rb +0 -520
  60. data/spec/unit/typesafe/config/simple_config_spec.rb +0 -112
  61. data/spec/unit/typesafe/config/token_spec.rb +0 -188
  62. data/spec/unit/typesafe/config/tokenizer_spec.rb +0 -801
@@ -1,832 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
- require 'test_utils'
5
- require 'hocon/config_parse_options'
6
- require 'hocon/config_syntax'
7
- require 'hocon/impl/abstract_config_object'
8
- require 'hocon/impl/resolve_context'
9
- require 'hocon/config_resolve_options'
10
- require 'hocon/config_error'
11
- require 'hocon/impl/simple_config_origin'
12
- require 'hocon/config_list'
13
- require 'hocon/impl/config_reference'
14
- require 'hocon/impl/path_parser'
15
- require 'hocon/impl/parseable'
16
- require 'hocon/config_factory'
17
-
18
- def parse_without_resolving(s)
19
- options = Hocon::ConfigParseOptions.defaults.
20
- set_origin_description("test conf string").
21
- set_syntax(Hocon::ConfigSyntax::CONF)
22
- Hocon::Impl::Parseable.new_string(s, options).parse_value
23
- end
24
-
25
- def parse(s)
26
- tree = parse_without_resolving(s)
27
-
28
- if tree.is_a?(Hocon::Impl::AbstractConfigObject)
29
- Hocon::Impl::ResolveContext.resolve(tree, tree,
30
- Hocon::ConfigResolveOptions.no_system)
31
- else
32
- tree
33
- end
34
- end
35
-
36
-
37
- describe "Config Parser" do
38
- context "invalid_conf_throws" do
39
- TestUtils.whitespace_variations(TestUtils::InvalidConf, false).each do |invalid|
40
- it "should raise an error for invalid config string '#{invalid.test}'" do
41
- TestUtils.add_offending_json_to_exception("config", invalid.test) {
42
- TestUtils.intercept(Hocon::ConfigError) {
43
- parse(invalid.test)
44
- }
45
- }
46
- end
47
- end
48
- end
49
-
50
- context "valid_conf_works" do
51
- TestUtils.whitespace_variations(TestUtils::ValidConf, true).each do |valid|
52
- it "should successfully parse config string '#{valid.test}'" do
53
- our_ast = TestUtils.add_offending_json_to_exception("config-conf", valid.test) {
54
- parse(valid.test)
55
- }
56
- # let's also check round-trip rendering
57
- rendered = our_ast.render
58
- reparsed = TestUtils.add_offending_json_to_exception("config-conf-reparsed", rendered) {
59
- parse(rendered)
60
- }
61
- expect(our_ast).to eq(reparsed)
62
- end
63
- end
64
- end
65
- end
66
-
67
- def parse_path(s)
68
- first_exception = nil
69
- second_exception = nil
70
- # parser first by wrapping into a whole document and using the regular parser
71
- result =
72
- begin
73
- tree = parse_without_resolving("[${#{s}}]")
74
- if tree.is_a?(Hocon::ConfigList)
75
- ref = tree[0]
76
- if ref.is_a?(Hocon::Impl::ConfigReference)
77
- ref.expression.path
78
- end
79
- end
80
- rescue Hocon::ConfigError => e
81
- first_exception = e
82
- nil
83
- end
84
-
85
- # also parse with the standalone path parser and be sure the outcome is the same
86
- begin
87
- should_be_same = Hocon::Impl::PathParser.parse_path(s)
88
- unless result == should_be_same
89
- raise "expected '#{result}' to equal '#{should_be_same}'"
90
- end
91
- rescue Hocon::ConfigError => e
92
- second_exception = e
93
- end
94
-
95
- if first_exception.nil? && (!second_exception.nil?)
96
- raise "only the standalone path parser threw: #{second_exception}"
97
- end
98
-
99
- if (!first_exception.nil?) && second_exception.nil?
100
- raise "only the whole-document parser threw: #{first_exception}"
101
- end
102
-
103
- if !first_exception.nil?
104
- raise first_exception
105
- end
106
- if !second_exception.nil?
107
- raise "wtf, should have thrown because not equal"
108
- end
109
-
110
- result
111
- end
112
-
113
- def test_path_parsing(first, second)
114
- it "'#{first}' should parse to same path as '#{second}'" do
115
- expect(TestUtils.path(*first)).to eq(parse_path(second))
116
- end
117
- end
118
-
119
- describe "Config Parser" do
120
- context "path_parsing" do
121
- test_path_parsing(["a"], "a")
122
- test_path_parsing(["a", "b"], "a.b")
123
- test_path_parsing(["a.b"], "\"a.b\"")
124
- test_path_parsing(["a."], "\"a.\"")
125
- test_path_parsing([".b"], "\".b\"")
126
- test_path_parsing(["true"], "true")
127
- test_path_parsing(["a"], " a ")
128
- test_path_parsing(["a ", "b"], " a .b")
129
- test_path_parsing(["a ", " b"], " a . b")
130
- test_path_parsing(["a b"], " a b")
131
- test_path_parsing(["a", "b.c", "d"], "a.\"b.c\".d")
132
- test_path_parsing(["3", "14"], "3.14")
133
- test_path_parsing(["3", "14", "159"], "3.14.159")
134
- test_path_parsing(["a3", "14"], "a3.14")
135
- test_path_parsing([""], "\"\"")
136
- test_path_parsing(["a", "", "b"], "a.\"\".b")
137
- test_path_parsing(["a", ""], "a.\"\"")
138
- test_path_parsing(["", "b"], "\"\".b")
139
- test_path_parsing(["", "", ""], ' "".""."" ')
140
- test_path_parsing(["a-c"], "a-c")
141
- test_path_parsing(["a_c"], "a_c")
142
- test_path_parsing(["-"], "\"-\"")
143
- test_path_parsing(["-"], "-")
144
- test_path_parsing(["-foo"], "-foo")
145
- test_path_parsing(["-10"], "-10")
146
-
147
- # here 10.0 is part of an unquoted string
148
- test_path_parsing(["foo10", "0"], "foo10.0")
149
- # here 10.0 is a number that gets value-concatenated
150
- test_path_parsing(["10", "0foo"], "10.0foo")
151
- # just a number
152
- test_path_parsing(["10", "0"], "10.0")
153
- # multiple-decimal number
154
- test_path_parsing(["1", "2", "3", "4"], "1.2.3.4")
155
-
156
- ["", " ", " \n \n ", "a.", ".b", "a..b", "a${b}c", "\"\".", ".\"\""].each do |invalid|
157
- begin
158
- it "should raise a ConfigBadPathError for '#{invalid}'" do
159
- TestUtils.intercept(Hocon::ConfigError::ConfigBadPathError) {
160
- parse_path(invalid)
161
- }
162
- end
163
- rescue => e
164
- $stderr.puts("failed on '#{invalid}'")
165
- raise e
166
- end
167
- end
168
- end
169
-
170
- it "should allow the last instance to win when duplicate keys are found" do
171
- obj = TestUtils.parse_config('{ "a" : 10, "a" : 11 } ')
172
-
173
- expect(obj.root.size).to eq(1)
174
- expect(obj.get_int("a")).to eq(11)
175
- end
176
-
177
- it "should merge maps when duplicate keys are found" do
178
- obj = TestUtils.parse_config('{ "a" : { "x" : 1, "y" : 2 }, "a" : { "x" : 42, "z" : 100 } }')
179
-
180
- expect(obj.root.size).to eq(1)
181
- expect(obj.get_object("a").size).to eq(3)
182
- expect(obj.get_int("a.x")).to eq(42)
183
- expect(obj.get_int("a.y")).to eq(2)
184
- expect(obj.get_int("a.z")).to eq(100)
185
- end
186
-
187
- it "should merge maps recursively when duplicate keys are found" do
188
- obj = TestUtils.parse_config('{ "a" : { "b" : { "x" : 1, "y" : 2 } }, "a" : { "b" : { "x" : 42, "z" : 100 } } }')
189
-
190
- expect(obj.root.size).to eq(1)
191
- expect(obj.get_object("a").size).to eq(1)
192
- expect(obj.get_object("a.b").size).to eq(3)
193
- expect(obj.get_int("a.b.x")).to eq(42)
194
- expect(obj.get_int("a.b.y")).to eq(2)
195
- expect(obj.get_int("a.b.z")).to eq(100)
196
- end
197
-
198
- it "should merge maps recursively when three levels of duplicate keys are found" do
199
- obj = TestUtils.parse_config('{ "a" : { "b" : { "c" : { "x" : 1, "y" : 2 } } }, "a" : { "b" : { "c" : { "x" : 42, "z" : 100 } } } }')
200
-
201
- expect(obj.root.size).to eq(1)
202
- expect(obj.get_object("a").size).to eq(1)
203
- expect(obj.get_object("a.b").size).to eq(1)
204
- expect(obj.get_object("a.b.c").size).to eq(3)
205
- expect(obj.get_int("a.b.c.x")).to eq(42)
206
- expect(obj.get_int("a.b.c.y")).to eq(2)
207
- expect(obj.get_int("a.b.c.z")).to eq(100)
208
- end
209
-
210
- it "should 'reset' a key when a null is found" do
211
- obj = TestUtils.parse_config('{ a : { b : 1 }, a : null, a : { c : 2 } }')
212
-
213
- expect(obj.root.size).to eq(1)
214
- expect(obj.get_object("a").size).to eq(1)
215
- expect(obj.get_int("a.c")).to eq(2)
216
- end
217
-
218
- it "should 'reset' a map key when a scalar is found" do
219
- obj = TestUtils.parse_config('{ a : { b : 1 }, a : 42, a : { c : 2 } }')
220
-
221
- expect(obj.root.size).to eq(1)
222
- expect(obj.get_object("a").size).to eq(1)
223
- expect(obj.get_int("a.c")).to eq(2)
224
- end
225
- end
226
-
227
- def drop_curlies(s)
228
- # drop the outside curly braces
229
- first = s.index('{')
230
- last = s.rindex('}')
231
- "#{s.slice(0..first)}#{s.slice(first+1..last)}#{s.slice(last + 1)}"
232
- end
233
-
234
- describe "Config Parser" do
235
- context "implied_comma_handling" do
236
- valids = ['
237
- // one line
238
- {
239
- a : y, b : z, c : [ 1, 2, 3 ]
240
- }', '
241
- // multiline but with all commas
242
- {
243
- a : y,
244
- b : z,
245
- c : [
246
- 1,
247
- 2,
248
- 3,
249
- ],
250
- }
251
- ', '
252
- // multiline with no commas
253
- {
254
- a : y
255
- b : z
256
- c : [
257
- 1
258
- 2
259
- 3
260
- ]
261
- }
262
- ']
263
-
264
- changes = [
265
- Proc.new { |s| s },
266
- Proc.new { |s| s.gsub("\n", "\n\n") },
267
- Proc.new { |s| s.gsub("\n", "\n\n\n") },
268
- Proc.new { |s| s.gsub(",\n", "\n,\n")},
269
- Proc.new { |s| s.gsub(",\n", "\n\n,\n\n") },
270
- Proc.new { |s| s.gsub("\n", " \n ") },
271
- Proc.new { |s| s.gsub(",\n", " \n \n , \n \n ") },
272
- Proc.new { |s| drop_curlies(s) }
273
- ]
274
-
275
- tested = 0
276
- changes.each do |change|
277
- valids.each do |v|
278
- tested += 1
279
- s = change.call(v)
280
- it "should handle commas and whitespaces properly for string '#{s}'" do
281
- obj = TestUtils.parse_config(s)
282
- expect(obj.root.size).to eq(3)
283
- expect(obj.get_string("a")).to eq("y")
284
- expect(obj.get_string("b")).to eq("z")
285
- expect(obj.get_int_list("c")).to eq([1,2,3])
286
- end
287
- end
288
- end
289
-
290
- it "should have run one test per change per valid string" do
291
- expect(tested).to eq(changes.length * valids.length)
292
- end
293
-
294
- context "should concatenate values when there is no newline or comma" do
295
- it "with no newline in array" do
296
- expect(TestUtils.parse_config(" { c : [ 1 2 3 ] } ").
297
- get_string_list("c")).to eq (["1 2 3"])
298
- end
299
-
300
- it "with no newline in array with quoted strings" do
301
- expect(TestUtils.parse_config(' { c : [ "4" "5" "6" ] } ').
302
- get_string_list("c")).to eq (["4 5 6"])
303
- end
304
-
305
- it "with no newline in object" do
306
- expect(TestUtils.parse_config(' { a : b c } ').
307
- get_string("a")).to eq ("b c")
308
- end
309
-
310
- it "with no newline at end" do
311
- expect(TestUtils.parse_config('a: b').
312
- get_string("a")).to eq ("b")
313
- end
314
-
315
- it "errors when no newline between keys" do
316
- TestUtils.intercept(Hocon::ConfigError) {
317
- TestUtils.parse_config('{ a : y b : z }')
318
- }
319
- end
320
-
321
- it "errors when no newline between quoted keys" do
322
- TestUtils.intercept(Hocon::ConfigError) {
323
- TestUtils.parse_config('{ "a" : "y" "b" : "z" }')
324
- }
325
- end
326
- end
327
- end
328
-
329
- it "should support keys with slashes" do
330
- obj = TestUtils.parse_config('/a/b/c=42, x/y/z : 32')
331
- expect(obj.get_int("/a/b/c")).to eq(42)
332
- expect(obj.get_int("x/y/z")).to eq(32)
333
- end
334
- end
335
-
336
- def line_number_test(num, text)
337
- it "should include the line number #{num} in the error message for invalid string '#{text}'" do
338
- e = TestUtils.intercept(Hocon::ConfigError) {
339
- TestUtils.parse_config(text)
340
- }
341
- if ! (e.message.include?("#{num}:"))
342
- raise "error message did not contain line '#{num}' '#{text.gsub("\n", "\\n")}' (#{e})"
343
- end
344
- end
345
- end
346
-
347
- describe "Config Parser" do
348
- context "line_numbers_in_errors" do
349
- # error is at the last char
350
- line_number_test(1, "}")
351
- line_number_test(2, "\n}")
352
- line_number_test(3, "\n\n}")
353
-
354
- # error is before a final newline
355
- line_number_test(1, "}\n")
356
- line_number_test(2, "\n}\n")
357
- line_number_test(3, "\n\n}\n")
358
-
359
- # with unquoted string
360
- line_number_test(1, "foo")
361
- line_number_test(2, "\nfoo")
362
- line_number_test(3, "\n\nfoo")
363
-
364
- # with quoted string
365
- line_number_test(1, "\"foo\"")
366
- line_number_test(2, "\n\"foo\"")
367
- line_number_test(3, "\n\n\"foo\"")
368
-
369
- # newlines in triple-quoted string should not hose up the numbering
370
- line_number_test(1, "a : \"\"\"foo\"\"\"}")
371
- line_number_test(2, "a : \"\"\"foo\n\"\"\"}")
372
- line_number_test(3, "a : \"\"\"foo\nbar\nbaz\"\"\"}")
373
- # newlines after the triple quoted string
374
- line_number_test(5, "a : \"\"\"foo\nbar\nbaz\"\"\"\n\n}")
375
- # triple quoted string ends in a newline
376
- line_number_test(6, "a : \"\"\"foo\nbar\nbaz\n\"\"\"\n\n}")
377
- # end in the middle of triple-quoted string
378
- line_number_test(5, "a : \"\"\"foo\n\n\nbar\n")
379
- end
380
-
381
- context "to_string_for_parseables" do
382
- # just to be sure the to_string don't throw, to get test coverage
383
- options = Hocon::ConfigParseOptions.defaults
384
- it "should allow to_s on File Parseable" do
385
- Hocon::Impl::Parseable.new_file("foo", options).to_s
386
- end
387
-
388
- it "should allow to_s on Resources Parseable" do
389
- Hocon::Impl::Parseable.new_resources("foo", options).to_s
390
- end
391
-
392
- it "should allow to_s on Resources Parseable" do
393
- Hocon::Impl::Parseable.new_string("foo", options).to_s
394
- end
395
-
396
- # NOTE: Skipping 'newURL', 'newProperties', 'newReader' tests here
397
- # because we don't implement them
398
- end
399
- end
400
-
401
- def assert_comments(comments, conf)
402
- it "should have comments #{comments} at root" do
403
- expect(conf.root.origin.comments).to eq(comments)
404
- end
405
- end
406
-
407
- def assert_comments_at_path(comments, conf, path)
408
- it "should have comments #{comments} at path #{path}" do
409
- expect(conf.get_value(path).origin.comments).to eq(comments)
410
- end
411
- end
412
-
413
- def assert_comments_at_path_index(comments, conf, path, index)
414
- it "should have comments #{comments} at path #{path} and index #{index}" do
415
- expect(conf.get_list(path).get(index).origin.comments).to eq(comments)
416
- end
417
- end
418
-
419
- describe "Config Parser" do
420
- context "track_comments_for_single_field" do
421
- # no comments
422
- conf0 = TestUtils.parse_config('
423
- {
424
- foo=10 }
425
- ')
426
- assert_comments_at_path([], conf0, "foo")
427
-
428
- # comment in front of a field is used
429
- conf1 = TestUtils.parse_config('
430
- { # Before
431
- foo=10 }
432
- ')
433
- assert_comments_at_path([" Before"], conf1, "foo")
434
-
435
- # comment with a blank line after is dropped
436
- conf2 = TestUtils.parse_config('
437
- { # BlankAfter
438
-
439
- foo=10 }
440
- ')
441
- assert_comments_at_path([], conf2, "foo")
442
-
443
- # comment in front of a field is used with no root {}
444
- conf3 = TestUtils.parse_config('
445
- # BeforeNoBraces
446
- foo=10
447
- ')
448
- assert_comments_at_path([" BeforeNoBraces"], conf3, "foo")
449
-
450
- # comment with a blank line after is dropped with no root {}
451
- conf4 = TestUtils.parse_config('
452
- # BlankAfterNoBraces
453
-
454
- foo=10
455
- ')
456
- assert_comments_at_path([], conf4, "foo")
457
-
458
- # comment same line after field is used
459
- conf5 = TestUtils.parse_config('
460
- {
461
- foo=10 # SameLine
462
- }
463
- ')
464
- assert_comments_at_path([" SameLine"], conf5, "foo")
465
-
466
- # comment before field separator is used
467
- conf6 = TestUtils.parse_config('
468
- {
469
- foo # BeforeSep
470
- =10
471
- }
472
- ')
473
- assert_comments_at_path([" BeforeSep"], conf6, "foo")
474
-
475
- # comment after field separator is used
476
- conf7 = TestUtils.parse_config('
477
- {
478
- foo= # AfterSep
479
- 10
480
- }
481
- ')
482
- assert_comments_at_path([" AfterSep"], conf7, "foo")
483
-
484
- # comment on next line is NOT used
485
- conf8 = TestUtils.parse_config('
486
- {
487
- foo=10
488
- # NextLine
489
- }
490
- ')
491
- assert_comments_at_path([], conf8, "foo")
492
-
493
- # comment before field separator on new line
494
- conf9 = TestUtils.parse_config('
495
- {
496
- foo
497
- # BeforeSepOwnLine
498
- =10
499
- }
500
- ')
501
- assert_comments_at_path([" BeforeSepOwnLine"], conf9, "foo")
502
-
503
- # comment after field separator on its own line
504
- conf10 = TestUtils.parse_config('
505
- {
506
- foo=
507
- # AfterSepOwnLine
508
- 10
509
- }
510
- ')
511
- assert_comments_at_path([" AfterSepOwnLine"], conf10, "foo")
512
-
513
- # comments comments everywhere
514
- conf11 = TestUtils.parse_config('
515
- {# Before
516
- foo
517
- # BeforeSep
518
- = # AfterSepSameLine
519
- # AfterSepNextLine
520
- 10 # AfterValue
521
- # AfterValueNewLine (should NOT be used)
522
- }
523
- ')
524
- assert_comments_at_path([" Before", " BeforeSep", " AfterSepSameLine", " AfterSepNextLine", " AfterValue"], conf11, "foo")
525
-
526
- # empty object
527
- conf12 = TestUtils.parse_config('# BeforeEmpty
528
- {} #AfterEmpty
529
- # NewLine
530
- ')
531
- assert_comments([" BeforeEmpty", "AfterEmpty"], conf12)
532
-
533
- # empty array
534
- conf13 = TestUtils.parse_config('
535
- foo=
536
- # BeforeEmptyArray
537
- [] #AfterEmptyArray
538
- # NewLine
539
- ')
540
- assert_comments_at_path([" BeforeEmptyArray", "AfterEmptyArray"], conf13, "foo")
541
-
542
- # array element
543
- conf14 = TestUtils.parse_config('
544
- foo=[
545
- # BeforeElement
546
- 10 # AfterElement
547
- ]
548
- ')
549
- assert_comments_at_path_index(
550
- [" BeforeElement", " AfterElement"], conf14, "foo", 0)
551
-
552
- # field with comma after it
553
- conf15 = TestUtils.parse_config('
554
- foo=10, # AfterCommaField
555
- ')
556
- assert_comments_at_path([" AfterCommaField"], conf15, "foo")
557
-
558
- # element with comma after it
559
- conf16 = TestUtils.parse_config('
560
- foo=[10, # AfterCommaElement
561
- ]
562
- ')
563
- assert_comments_at_path_index([" AfterCommaElement"], conf16, "foo", 0)
564
-
565
- # field with comma after it but comment isn't on the field's line, so not used
566
- conf17 = TestUtils.parse_config('
567
- foo=10
568
- , # AfterCommaFieldNotUsed
569
- ')
570
- assert_comments_at_path([], conf17, "foo")
571
-
572
- # element with comma after it but comment isn't on the field's line, so not used
573
- conf18 = TestUtils.parse_config('
574
- foo=[10
575
- , # AfterCommaElementNotUsed
576
- ]
577
- ')
578
- assert_comments_at_path_index([], conf18, "foo", 0)
579
-
580
- # comment on new line, before comma, should not be used
581
- conf19 = TestUtils.parse_config('
582
- foo=10
583
- # BeforeCommaFieldNotUsed
584
- ,
585
- ')
586
- assert_comments_at_path([], conf19, "foo")
587
-
588
- # comment on new line, before comma, should not be used
589
- conf20 = TestUtils.parse_config('
590
- foo=[10
591
- # BeforeCommaElementNotUsed
592
- ,
593
- ]
594
- ')
595
- assert_comments_at_path_index([], conf20, "foo", 0)
596
-
597
- # comment on same line before comma
598
- conf21 = TestUtils.parse_config('
599
- foo=10 # BeforeCommaFieldSameLine
600
- ,
601
- ')
602
- assert_comments_at_path([" BeforeCommaFieldSameLine"], conf21, "foo")
603
-
604
- # comment on same line before comma
605
- conf22 = TestUtils.parse_config('
606
- foo=[10 # BeforeCommaElementSameLine
607
- ,
608
- ]
609
- ')
610
- assert_comments_at_path_index([" BeforeCommaElementSameLine"], conf22, "foo", 0)
611
- end
612
-
613
- context "track_comments_for_multiple_fields" do
614
- # nested objects
615
- conf5 = TestUtils.parse_config('
616
- # Outside
617
- bar {
618
- # Ignore me
619
-
620
- # Middle
621
- # two lines
622
- baz {
623
- # Inner
624
- foo=10 # AfterInner
625
- # This should be ignored
626
- } # AfterMiddle
627
- # ignored
628
- } # AfterOutside
629
- # ignored!
630
- ')
631
- assert_comments_at_path([" Inner", " AfterInner"], conf5, "bar.baz.foo")
632
- assert_comments_at_path([" Middle", " two lines", " AfterMiddle"], conf5, "bar.baz")
633
- assert_comments_at_path([" Outside", " AfterOutside"], conf5, "bar")
634
-
635
- # multiple fields
636
- conf6 = TestUtils.parse_config('{
637
- # this is not with a field
638
-
639
- # this is field A
640
- a : 10,
641
- # this is field B
642
- b : 12 # goes with field B which has no comma
643
- # this is field C
644
- c : 14, # goes with field C after comma
645
- # not used
646
- # this is not used
647
- # nor is this
648
- # multi-line block
649
-
650
- # this is with field D
651
- # this is with field D also
652
- d : 16
653
-
654
- # this is after the fields
655
- }')
656
- assert_comments_at_path([" this is field A"], conf6, "a")
657
- assert_comments_at_path([" this is field B", " goes with field B which has no comma"], conf6, "b")
658
- assert_comments_at_path([" this is field C", " goes with field C after comma"], conf6, "c")
659
- assert_comments_at_path([" this is with field D", " this is with field D also"], conf6, "d")
660
-
661
- # array
662
- conf7 = TestUtils.parse_config('
663
- # before entire array
664
- array = [
665
- # goes with 0
666
- 0,
667
- # goes with 1
668
- 1, # with 1 after comma
669
- # goes with 2
670
- 2 # no comma after 2
671
- # not with anything
672
- ] # after entire array
673
- ')
674
- assert_comments_at_path_index([" goes with 0"], conf7, "array", 0)
675
- assert_comments_at_path_index([" goes with 1", " with 1 after comma"], conf7, "array", 1)
676
- assert_comments_at_path_index([" goes with 2", " no comma after 2"], conf7, "array", 2)
677
- assert_comments_at_path([" before entire array", " after entire array"], conf7, "array")
678
-
679
- # properties-like syntax
680
- conf8 = TestUtils.parse_config('
681
- # ignored comment
682
-
683
- # x.y comment
684
- x.y = 10
685
- # x.z comment
686
- x.z = 11
687
- # x.a comment
688
- x.a = 12
689
- # a.b comment
690
- a.b = 14
691
- a.c = 15
692
- a.d = 16 # a.d comment
693
- # ignored comment
694
- ')
695
-
696
- assert_comments_at_path([" x.y comment"], conf8, "x.y")
697
- assert_comments_at_path([" x.z comment"], conf8, "x.z")
698
- assert_comments_at_path([" x.a comment"], conf8, "x.a")
699
- assert_comments_at_path([" a.b comment"], conf8, "a.b")
700
- assert_comments_at_path([], conf8, "a.c")
701
- assert_comments_at_path([" a.d comment"], conf8, "a.d")
702
- # here we're concerned that comments apply only to leaf
703
- # nodes, not to parent objects.
704
- assert_comments_at_path([], conf8, "x")
705
- assert_comments_at_path([], conf8, "a")
706
- end
707
-
708
-
709
- it "includeFile" do
710
- conf = Hocon::ConfigFactory.parse_string("include file(" +
711
- TestUtils.json_quoted_resource_file("test01") + ")")
712
-
713
- # should have loaded conf, json... skipping properties
714
- expect(conf.get_int("ints.fortyTwo")).to eq(42)
715
- expect(conf.get_int("fromJson1")).to eq(1)
716
- end
717
-
718
- it "includeFileWithExtension" do
719
- conf = Hocon::ConfigFactory.parse_string("include file(" +
720
- TestUtils.json_quoted_resource_file("test01.conf") + ")")
721
-
722
- expect(conf.get_int("ints.fortyTwo")).to eq(42)
723
- expect(conf.has_path?("fromJson1")).to eq(false)
724
- expect(conf.has_path?("fromProps.abc")).to eq(false)
725
- end
726
-
727
- it "includeFileWhitespaceInsideParens" do
728
- conf = Hocon::ConfigFactory.parse_string("include file( \n " +
729
- TestUtils.json_quoted_resource_file("test01") + " \n )")
730
-
731
- # should have loaded conf, json... NOT properties
732
- expect(conf.get_int("ints.fortyTwo")).to eq(42)
733
- expect(conf.get_int("fromJson1")).to eq(1)
734
- end
735
-
736
- it "includeFileNoWhitespaceOutsideParens" do
737
- e = TestUtils.intercept(Hocon::ConfigError::ConfigParseError) {
738
- Hocon::ConfigFactory.parse_string("include file (" +
739
- TestUtils.json_quoted_resource_file("test01") + ")")
740
- }
741
- expect(e.message.include?("expecting include parameter")).to eq(true)
742
- end
743
-
744
- it "includeFileNotQuoted" do
745
- # this test cannot work on Windows
746
- f = TestUtils.resource_file("test01")
747
- if (f.to_s.include?("\\"))
748
- $stderr.puts("includeFileNotQuoted test skipped on Windows")
749
- else
750
- e = TestUtils.intercept(Hocon::ConfigError::ConfigParseError) {
751
- Hocon::ConfigFactory.parse_string("include file(" + f + ")")
752
- }
753
- expect(e.message.include?("expecting include parameter")).to eq(true)
754
- end
755
- end
756
-
757
- it "includeFileNotQuotedAndSpecialChar" do
758
- f = TestUtils.resource_file("test01")
759
- if (f.to_s.include?("\\"))
760
- $stderr.puts("includeFileNotQuoted test skipped on Windows")
761
- else
762
- e = TestUtils.intercept(Hocon::ConfigError::ConfigParseError) {
763
- Hocon::ConfigFactory.parse_string("include file(:" + f + ")")
764
- }
765
- expect(e.message.include?("expecting a quoted string")).to eq(true)
766
- end
767
-
768
- end
769
-
770
- it "includeFileUnclosedParens" do
771
- e = TestUtils.intercept(Hocon::ConfigError::ConfigParseError) {
772
- Hocon::ConfigFactory.parse_string("include file(" + TestUtils.json_quoted_resource_file("test01") + " something")
773
- }
774
- expect(e.message.include?("expecting a close paren")).to eq(true)
775
- end
776
-
777
- # Skipping 'includeURLBasename' because we don't support URLs
778
- # Skipping 'includeURLWithExtension' because we don't support URLs
779
- # Skipping 'includeURLInvalid' because we don't support URLs
780
- # Skipping 'includeResources' because we don't support classpath resources
781
- # Skipping 'includeURLHeuristically' because we don't support URLs
782
- # Skipping 'includeURLBasenameHeuristically' because we don't support URLs
783
-
784
- it "shouldacceptUTF8FileNames" do
785
- skip('UTF-8 filenames not currently supported') do
786
- expect { Hocon::ConfigFactory.parse_file(TestUtils.resource_file("ᚠᛇᚻ.conf")) }.to raise_error
787
- end
788
- end
789
-
790
- it "acceptsUTF8FileContents" do
791
- # utf8.conf is UTF-8 with no BOM
792
- rune_utf8 = "\u16EB\u16D2\u16E6\u16A6\u16EB\u16A0\u16B1\u16A9\u16A0\u16A2"
793
- conf = Hocon::ConfigFactory.parse_file(TestUtils.resource_file("utf8.conf"))
794
- expect(conf.get_string("\u16A0\u16C7\u16BB")).to eq(rune_utf8)
795
- end
796
-
797
- it "shouldacceptUTF16FileContents" do
798
- skip('supporting UTF-16 requires appropriate BOM detection during parsing') do
799
- # utf16.conf is UTF-16LE with a BOM
800
- expect { Hocon::ConfigFactory.parse_file(TestUtils.resource_file("utf16.conf")) }.to raise_error
801
- end
802
- end
803
-
804
- it "acceptBOMStartingFile" do
805
- # BOM at start of file should be ignored
806
- conf = Hocon::ConfigFactory.parse_file(TestUtils.resource_file("bom.conf"))
807
- expect(conf.get_string("foo")).to eq("bar")
808
- end
809
-
810
- it "acceptBOMInStringValue" do
811
- # BOM inside quotes should be preserved, just as other whitespace would be
812
- conf = Hocon::ConfigFactory.parse_string("foo=\"\uFEFF\uFEFF\"")
813
- expect(conf.get_string("foo")).to eq("\uFEFF\uFEFF")
814
- end
815
-
816
- it "acceptBOMWhitespace" do
817
- skip("BOM not parsing properly yet; not fixing this now because it most likely only affects windows") do
818
- # BOM here should be treated like other whitespace (ignored, since no quotes)
819
- conf = Hocon::ConfigFactory.parse_string("foo= \uFEFFbar\uFEFF")
820
- expect(conf.get_string("foo")).to eq("bar")
821
- end
822
- end
823
-
824
- it "acceptMultiPeriodNumericPath" do
825
- conf1 = Hocon::ConfigFactory.parse_string("0.1.2.3=foobar1")
826
- expect(conf1.get_string("0.1.2.3")).to eq("foobar1")
827
- conf2 = Hocon::ConfigFactory.parse_string("0.1.2.3.ABC=foobar2")
828
- expect(conf2.get_string("0.1.2.3.ABC")).to eq("foobar2")
829
- conf3 = Hocon::ConfigFactory.parse_string("ABC.0.1.2.3=foobar3")
830
- expect(conf3.get_string("ABC.0.1.2.3")).to eq("foobar3")
831
- end
832
- end