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,576 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'hocon'
5
+ require 'hocon/parser/config_document_factory'
6
+ require 'hocon/config_value_factory'
7
+ require 'test_utils'
8
+
9
+ describe "ConfigDocument" do
10
+ ConfigDocumentFactory = Hocon::Parser::ConfigDocumentFactory
11
+ ConfigParseOptions = Hocon::ConfigParseOptions
12
+ ConfigSyntax = Hocon::ConfigSyntax
13
+ SimpleConfigDocument = Hocon::Impl::SimpleConfigDocument
14
+ ConfigValueFactory = Hocon::ConfigValueFactory
15
+
16
+ shared_examples_for "config document replace JSON test" do
17
+ let (:config_document) { ConfigDocumentFactory.parse_string(orig_text, ConfigParseOptions.defaults.set_syntax(ConfigSyntax::JSON)) }
18
+ it "should correctly render the parsed JSON document" do
19
+ expect(config_document.render).to eq(orig_text)
20
+ end
21
+
22
+ it "should perform a successful replacement on the parsed JSON document" do
23
+ new_document = config_document.set_value(replace_path, new_value)
24
+ #expect(new_document).to be_a(SimpleConfigDocument)
25
+ expect(new_document.render).to eq(final_text)
26
+ end
27
+ end
28
+
29
+ shared_examples_for "config document replace CONF test" do
30
+ let (:config_document) { ConfigDocumentFactory.parse_string(orig_text) }
31
+ it "should correctly render the parsed CONF document" do
32
+ expect(config_document.render).to eq(orig_text)
33
+ end
34
+
35
+ it "should perform a successful replacement on the parsed CONF document" do
36
+ new_document = config_document.set_value(replace_path, new_value)
37
+ #expect(new_document).to be_a(SimpleConfigDocument)
38
+ expect(new_document.render).to eq(final_text)
39
+ end
40
+ end
41
+
42
+ context "ConfigDocument replace" do
43
+ let (:orig_text) {
44
+ '{
45
+ "a":123,
46
+ "b": 123.456,
47
+ "c": true,
48
+ "d": false,
49
+ "e": null,
50
+ "f": "a string",
51
+ "g": [1,2,3,4,5],
52
+ "h": {
53
+ "a": 123,
54
+ "b": {
55
+ "a": 12
56
+ },
57
+ "c": [1, 2, 3, {"a": "b"}, [1,2,3]]
58
+ }
59
+ }'
60
+ }
61
+ context "parsing/replacement with a very simple map" do
62
+ let(:orig_text) { '{"a":1}' }
63
+ let(:final_text) { '{"a":2}' }
64
+ let (:new_value) { "2" }
65
+ let (:replace_path) { "a" }
66
+ include_examples "config document replace CONF test"
67
+ include_examples "config document replace JSON test"
68
+ end
69
+
70
+ context "parsing/replacement with a map without surrounding braces" do
71
+ let (:orig_text) { "a: b\nc = d" }
72
+ let (:final_text) { "a: b\nc = 12" }
73
+ let (:new_value) { "12" }
74
+ let (:replace_path) { "c" }
75
+ include_examples "config document replace CONF test"
76
+ end
77
+
78
+ context "parsing/replacement with a complicated map" do
79
+ let (:final_text) {
80
+ '{
81
+ "a":123,
82
+ "b": 123.456,
83
+ "c": true,
84
+ "d": false,
85
+ "e": null,
86
+ "f": "a string",
87
+ "g": [1,2,3,4,5],
88
+ "h": {
89
+ "a": 123,
90
+ "b": {
91
+ "a": "i am now a string"
92
+ },
93
+ "c": [1, 2, 3, {"a": "b"}, [1,2,3]]
94
+ }
95
+ }'
96
+ }
97
+ let (:new_value) { '"i am now a string"' }
98
+ let (:replace_path) { "h.b.a" }
99
+ include_examples "config document replace CONF test"
100
+ include_examples "config document replace JSON test"
101
+ end
102
+
103
+ context "replacing values with maps" do
104
+ let (:final_text) {
105
+ '{
106
+ "a":123,
107
+ "b": 123.456,
108
+ "c": true,
109
+ "d": false,
110
+ "e": null,
111
+ "f": "a string",
112
+ "g": [1,2,3,4,5],
113
+ "h": {
114
+ "a": 123,
115
+ "b": {
116
+ "a": {"a":"b", "c":"d"}
117
+ },
118
+ "c": [1, 2, 3, {"a": "b"}, [1,2,3]]
119
+ }
120
+ }' }
121
+ let (:new_value) { '{"a":"b", "c":"d"}' }
122
+ let (:replace_path) { "h.b.a" }
123
+ include_examples "config document replace CONF test"
124
+ include_examples "config document replace JSON test"
125
+ end
126
+
127
+ context "replacing values with arrays" do
128
+ let (:final_text) {
129
+ '{
130
+ "a":123,
131
+ "b": 123.456,
132
+ "c": true,
133
+ "d": false,
134
+ "e": null,
135
+ "f": "a string",
136
+ "g": [1,2,3,4,5],
137
+ "h": {
138
+ "a": 123,
139
+ "b": {
140
+ "a": [1,2,3,4,5]
141
+ },
142
+ "c": [1, 2, 3, {"a": "b"}, [1,2,3]]
143
+ }
144
+ }' }
145
+ let (:new_value) { "[1,2,3,4,5]" }
146
+ let (:replace_path) { "h.b.a" }
147
+ include_examples "config document replace CONF test"
148
+ include_examples "config document replace JSON test"
149
+ end
150
+
151
+ context "replacing values with concatenations" do
152
+ let (:final_text) {
153
+ '{
154
+ "a":123,
155
+ "b": 123.456,
156
+ "c": true,
157
+ "d": false,
158
+ "e": null,
159
+ "f": "a string",
160
+ "g": [1,2,3,4,5],
161
+ "h": {
162
+ "a": 123,
163
+ "b": {
164
+ "a": this is a concatenation 123 456 {a:b} [1,2,3] {a: this is another 123 concatenation null true}
165
+ },
166
+ "c": [1, 2, 3, {"a": "b"}, [1,2,3]]
167
+ }
168
+ }' }
169
+ let (:new_value) { "this is a concatenation 123 456 {a:b} [1,2,3] {a: this is another 123 concatenation null true}" }
170
+ let (:replace_path) { "h.b.a" }
171
+ include_examples "config document replace CONF test"
172
+ end
173
+ end
174
+
175
+ context "config document multi element duplicates removed" do
176
+ it "should remove all duplicates when setting a value" do
177
+ orig_text = "{a: b, a.b.c: d, a: e}"
178
+ config_doc = ConfigDocumentFactory.parse_string(orig_text)
179
+ expect(config_doc.set_value("a", "2").render).to eq("{a: 2}")
180
+ end
181
+
182
+ it "should keep a trailing comma if succeeding elements were removed in CONF" do
183
+ orig_text = "{a: b, a: e, a.b.c: d}"
184
+ config_doc = ConfigDocumentFactory.parse_string(orig_text)
185
+ expect(config_doc.set_value("a", "2").render).to eq("{a: 2, }")
186
+ end
187
+
188
+ it "should add the setting if only a multi-element duplicate exists" do
189
+ orig_text = "{a.b.c: d}"
190
+ config_doc = ConfigDocumentFactory.parse_string(orig_text)
191
+ expect(config_doc.set_value("a", "2").render).to eq("{ a : 2}")
192
+ end
193
+ end
194
+
195
+ context "config document set new value brace root" do
196
+ let (:orig_text) { "{\n\t\"a\":\"b\",\n\t\"c\":\"d\"\n}" }
197
+ let (:new_value) { "\"f\"" }
198
+ let (:replace_path) { "\"e\"" }
199
+
200
+ context "set a new value in CONF" do
201
+ let (:final_text) { "{\n\t\"a\":\"b\",\n\t\"c\":\"d\"\n\t\"e\" : \"f\"\n}" }
202
+ include_examples "config document replace CONF test"
203
+ end
204
+
205
+ context "set a new value in JSON" do
206
+ let (:final_text) { "{\n\t\"a\":\"b\",\n\t\"c\":\"d\",\n\t\"e\" : \"f\"\n}" }
207
+ include_examples "config document replace JSON test"
208
+ end
209
+ end
210
+
211
+ context "config document set new value no braces" do
212
+ let (:orig_text) { "\"a\":\"b\",\n\"c\":\"d\"\n" }
213
+ let (:final_text) { "\"a\":\"b\",\n\"c\":\"d\"\n\"e\" : \"f\"\n" }
214
+ let (:new_value) { "\"f\"" }
215
+ let (:replace_path) { "\"e\"" }
216
+
217
+ include_examples "config document replace CONF test"
218
+ end
219
+
220
+ context "config document set new value multi level CONF" do
221
+ let (:orig_text) { "a:b\nc:d" }
222
+ let (:final_text) { "a:b\nc:d\ne : {\n f : {\n g : 12\n }\n}" }
223
+ let (:new_value) { "12" }
224
+ let (:replace_path) { "e.f.g" }
225
+
226
+ include_examples "config document replace CONF test"
227
+ end
228
+
229
+ context "config document set new value multi level JSON" do
230
+ let (:orig_text) { "{\"a\":\"b\",\n\"c\":\"d\"}" }
231
+ let (:final_text) { "{\"a\":\"b\",\n\"c\":\"d\",\n \"e\" : {\n \"f\" : {\n \"g\" : 12\n }\n }}" }
232
+ let (:new_value) { "12" }
233
+ let (:replace_path) { "e.f.g" }
234
+
235
+ include_examples "config document replace JSON test"
236
+ end
237
+
238
+ context "config document set new config value" do
239
+ let (:orig_text) { "{\"a\": \"b\"}" }
240
+ let (:final_text) { "{\"a\": 12}" }
241
+ let (:config_doc_hocon) { ConfigDocumentFactory.parse_string(orig_text) }
242
+ let (:config_doc_json) { ConfigDocumentFactory.parse_string(orig_text, ConfigParseOptions.defaults.set_syntax(ConfigSyntax::JSON)) }
243
+ let (:new_value) { ConfigValueFactory.from_any_ref(12) }
244
+
245
+ it "should successfuly render the original text from both documents" do
246
+ expect(config_doc_hocon.render).to eq(orig_text)
247
+ expect(config_doc_json.render).to eq(orig_text)
248
+ end
249
+
250
+ it "should succesfully set a new value on both documents" do
251
+ expect(config_doc_hocon.set_config_value("a", new_value).render).to eq(final_text)
252
+ expect(config_doc_json.set_config_value("a", new_value).render).to eq(final_text)
253
+ end
254
+ end
255
+
256
+ context "config document has value" do
257
+ let (:orig_text) { "{a: b, a.b.c.d: e, c: {a: {b: c}}}" }
258
+ let (:config_doc) { ConfigDocumentFactory.parse_string(orig_text) }
259
+
260
+ it "should return true on paths that exist in the document" do
261
+ expect(config_doc.has_value?("a")).to be_truthy
262
+ expect(config_doc.has_value?("a.b.c")).to be_truthy
263
+ expect(config_doc.has_value?("c.a.b")).to be_truthy
264
+ end
265
+
266
+ it "should return false on paths that don't exist in the document" do
267
+ expect(config_doc.has_value?("c.a.b.c")).to be_falsey
268
+ expect(config_doc.has_value?("a.b.c.d.e")).to be_falsey
269
+ expect(config_doc.has_value?("this.does.not.exist")).to be_falsey
270
+ end
271
+ end
272
+
273
+ context "config document remove value" do
274
+ let (:orig_text) { "{a: b, a.b.c.d: e, c: {a: {b: c}}}" }
275
+ let (:config_doc) { ConfigDocumentFactory.parse_string(orig_text) }
276
+
277
+ it "should remove a top-level setting with a simple value" do
278
+ expect(config_doc.remove_value("a").render).to eq("{c: {a: {b: c}}}")
279
+ end
280
+
281
+ it "should remove a top-level setting with a complex value" do
282
+ expect(config_doc.remove_value("c").render).to eq("{a: b, a.b.c.d: e, }")
283
+ end
284
+
285
+ it "should do nothing if the setting does not exist" do
286
+ expect(config_doc.remove_value("this.does.not.exist")).to eq(config_doc)
287
+ end
288
+ end
289
+
290
+ context "config document remove value JSON" do
291
+ it "should not leave a trailing comma when removing a value in JSON" do
292
+ orig_text = '{"a": "b", "c": "d"}'
293
+ config_doc = ConfigDocumentFactory.parse_string(orig_text, ConfigParseOptions.defaults.set_syntax(ConfigSyntax::JSON))
294
+ expect(config_doc.remove_value("c").render).to eq('{"a": "b" }')
295
+ end
296
+ end
297
+
298
+ context "config document remove multiple" do
299
+ it "should remove duplicate nested keys" do
300
+ orig_text = "a { b: 42 }, a.b = 43, a { b: { c: 44 } }"
301
+ config_doc = ConfigDocumentFactory.parse_string(orig_text)
302
+ removed = config_doc.remove_value("a.b")
303
+ expect(removed.render).to eq("a { }, a { }")
304
+ end
305
+ end
306
+
307
+ context "config document remove overridden" do
308
+ it "should remove all instances of keys even if overridden by a top-level key/value pair" do
309
+ orig_text = "a { b: 42 }, a.b = 43, a { b: { c: 44 } }, a : 57 "
310
+ config_doc = ConfigDocumentFactory.parse_string(orig_text)
311
+ removed = config_doc.remove_value("a.b")
312
+ expect(removed.render).to eq("a { }, a { }, a : 57 ")
313
+ end
314
+ end
315
+
316
+ context "config document remove nested" do
317
+ it "should remove nested keys if specified" do
318
+ orig_text = "a { b: 42 }, a.b = 43, a { b: { c: 44 } }"
319
+ config_doc = ConfigDocumentFactory.parse_string(orig_text)
320
+ removed = config_doc.remove_value("a.b.c")
321
+ expect(removed.render).to eq("a { b: 42 }, a.b = 43, a { b: { } }")
322
+ end
323
+ end
324
+
325
+ context "config document array failures" do
326
+ let (:orig_text) { "[1, 2, 3, 4, 5]" }
327
+ let (:document) { ConfigDocumentFactory.parse_string(orig_text) }
328
+
329
+ it "should throw when set_value is called and there is an array at the root" do
330
+ e = TestUtils.intercept(Hocon::ConfigError) { document.set_value("a", "1") }
331
+ expect(e.message).to include("ConfigDocument had an array at the root level")
332
+ end
333
+
334
+ it "should throw when has_value is called and there is an array at the root" do
335
+ e = TestUtils.intercept(Hocon::ConfigError) { document.has_value?("a") }
336
+ expect(e.message).to include("ConfigDocument had an array at the root level")
337
+ end
338
+
339
+ it "should throw when remove_value is called and there is an array at the root" do
340
+ e = TestUtils.intercept(Hocon::ConfigError) { document.remove_value("a") }
341
+ expect(e.message).to include("ConfigDocument had an array at the root level")
342
+ end
343
+ end
344
+
345
+ context "config document JSON replace failure" do
346
+ it "should fail when trying to replace with a value using HOCON syntax in JSON" do
347
+ orig_text = "{\"foo\": \"bar\", \"baz\": \"qux\"}"
348
+ document = ConfigDocumentFactory.parse_string(orig_text, ConfigParseOptions.defaults.set_syntax(ConfigSyntax::JSON))
349
+
350
+ e = TestUtils.intercept(Hocon::ConfigError) { document.set_value("foo", "unquoted") }
351
+ expect(e.message).to include("Token not allowed in valid JSON")
352
+ end
353
+ end
354
+
355
+ context "config document JSON replace with concatenation failure" do
356
+ it "should fail when trying to add a concatenation into a JSON document" do
357
+ orig_text = "{\"foo\": \"bar\", \"baz\": \"qux\"}"
358
+ document = ConfigDocumentFactory.parse_string(orig_text, ConfigParseOptions.defaults.set_syntax(ConfigSyntax::JSON))
359
+
360
+ e = TestUtils.intercept(Hocon::ConfigError) { document.set_value("foo", "1 2 3 concatenation") }
361
+ expect(e.message).to include("Parsing JSON and the value set in setValue was either a concatenation or had trailing whitespace, newlines, or comments")
362
+ end
363
+ end
364
+
365
+ context "config document file parse" do
366
+ let (:config_document) { ConfigDocumentFactory.parse_file(TestUtils.resource_file("test01.conf")) }
367
+ let (:file_text) {
368
+ file = File.open(TestUtils.resource_file("test01.conf"), "rb")
369
+ contents = file.read
370
+ file.close
371
+ contents
372
+ }
373
+
374
+ it "should correctly parse from a file" do
375
+ expect(config_document.render).to eq(file_text)
376
+ end
377
+ end
378
+
379
+ # skipping reader parsing, since we don't support that in ruby hocon
380
+
381
+ context "config document indentation single line object" do
382
+ it "should properly indent a value in a single-line map" do
383
+ orig_text = "a { b: c }"
384
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
385
+ expect(config_document.set_value("a.d", "e").render).to eq("a { b: c, d : e }")
386
+ end
387
+
388
+ it "should properly indent a value in the top-level when it is on a single line" do
389
+ orig_text = "a { b: c }, d: e"
390
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
391
+ expect(config_document.set_value("f", "g").render).to eq("a { b: c }, d: e, f : g")
392
+ end
393
+
394
+ it "should not preserve trailing commas" do
395
+ orig_text = "a { b: c }, d: e,"
396
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
397
+ expect(config_document.set_value("f", "g").render).to eq("a { b: c }, d: e, f : g")
398
+ end
399
+
400
+ it "should add necessary keys along the path to the value and properly space them" do
401
+ orig_text = "a { b: c }, d: e,"
402
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
403
+ expect(config_document.set_value("f.g.h", "i").render).to eq("a { b: c }, d: e, f : { g : { h : i } }")
404
+ end
405
+
406
+ it "should properly indent keys added to the top-level with curly braces" do
407
+ orig_text = "{a { b: c }, d: e}"
408
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
409
+ expect(config_document.set_value("f", "g").render).to eq("{a { b: c }, d: e, f : g}")
410
+ end
411
+
412
+ it "should add necessary keys along the path to the value and properly space them when the root has braces" do
413
+ orig_text = "{a { b: c }, d: e}"
414
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
415
+ expect(config_document.set_value("f.g.h", "i").render).to eq("{a { b: c }, d: e, f : { g : { h : i } }}")
416
+ end
417
+ end
418
+
419
+ context "config document indentation multi line object" do
420
+ context "document with no trailing newlines" do
421
+ let (:orig_text) { "a {\n b: c\n}" }
422
+ let (:config_document) { ConfigDocumentFactory.parse_string(orig_text) }
423
+
424
+ it "should properly indent a value in a multi-line map" do
425
+ expect(config_document.set_value("a.e", "f").render).to eq("a {\n b: c\n e : f\n}")
426
+ end
427
+
428
+ it "should properly add/indent any necessary objects along the way to the value" do
429
+ expect(config_document.set_value("a.d.e.f", "g").render).to eq("a {\n b: c\n d : {\n e : {\n f : g\n }\n }\n}")
430
+ end
431
+ end
432
+
433
+ context "document with multi-line root" do
434
+ let (:orig_text) { "a {\n b: c\n}\n" }
435
+ let (:config_document) { ConfigDocumentFactory.parse_string(orig_text) }
436
+
437
+ it "should properly indent a value at the root with multiple lines" do
438
+ expect(config_document.set_value("d", "e").render).to eq("a {\n b: c\n}\nd : e\n")
439
+ end
440
+
441
+ it "should properly add/indent any necessary objects along the way to the value" do
442
+ expect(config_document.set_value("d.e.f", "g").render).to eq("a {\n b: c\n}\nd : {\n e : {\n f : g\n }\n}\n")
443
+ end
444
+ end
445
+ end
446
+
447
+ context "config document indentation nested" do
448
+ it "should properly space a new key/value pair in a nested map in a single-line document" do
449
+ orig_text = "a { b { c { d: e } } }"
450
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
451
+ expect(config_document.set_value("a.b.c.f", "g").render).to eq("a { b { c { d: e, f : g } } }")
452
+ end
453
+
454
+ it "should properly space a new key/value pair in a nested map in a multi-line document" do
455
+ orig_text = "a {\n b {\n c {\n d: e\n }\n }\n}"
456
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
457
+ expect(config_document.set_value("a.b.c.f", "g").render).to eq("a {\n b {\n c {\n d: e\n f : g\n }\n }\n}")
458
+ end
459
+ end
460
+
461
+ context "config document indentation empty object" do
462
+ it "should properly space a new key/value pair in a single-line empty object" do
463
+ orig_text = "a { }"
464
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
465
+ expect(config_document.set_value("a.b", "c").render).to eq("a { b : c }")
466
+ end
467
+
468
+ it "should properly indent a new key/value pair in a multi-line empty object" do
469
+ orig_text = "a {\n b {\n }\n}"
470
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
471
+ expect(config_document.set_value("a.b.c", "d").render).to eq("a {\n b {\n c : d\n }\n}")
472
+ end
473
+ end
474
+
475
+ context "config document indentation multi line value" do
476
+ let (:orig_text) { "a {\n b {\n c {\n d: e\n }\n }\n}" }
477
+ let (:config_document) { ConfigDocumentFactory.parse_string(orig_text) }
478
+
479
+ it "should successfully insert and indent a multi-line object" do
480
+ expect(config_document.set_value("a.b.c.f", "{\n g: h\n i: j\n k: {\n l: m\n }\n}").render
481
+ ).to eq("a {\n b {\n c {\n d: e\n f : {\n g: h\n i: j\n k: {\n l: m\n }\n }\n }\n }\n}")
482
+ end
483
+
484
+ it "should successfully insert a concatenation with a multi-line array" do
485
+ expect(config_document.set_value("a.b.c.f", "12 13 [1,\n2,\n3,\n{\n a:b\n}]").render
486
+ ).to eq("a {\n b {\n c {\n d: e\n f : 12 13 [1,\n 2,\n 3,\n {\n a:b\n }]\n }\n }\n}")
487
+ end
488
+ end
489
+
490
+ context "config document indentation multi line value single line object" do
491
+ it "should get weird indentation when adding a multi-line value to a single-line object" do
492
+ orig_text = "a { b { } }"
493
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
494
+ expect(config_document.set_value("a.b.c", "{\n c:d\n}").render).to eq("a { b { c : {\n c:d\n } } }")
495
+ end
496
+ end
497
+
498
+ context "config document indentation single line object containing multi line value" do
499
+ it "should treat an object with no new-lines outside of its values as a single-line object" do
500
+ orig_text = "a { b {\n c: d\n} }"
501
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
502
+ expect(config_document.set_value("a.e", "f").render).to eq("a { b {\n c: d\n}, e : f }")
503
+ end
504
+ end
505
+
506
+ context "config document indentation replacing with multi line value" do
507
+ it "should properly indent a multi-line value when replacing a single-line value" do
508
+ orig_text = "a {\n b {\n c : 22\n }\n}"
509
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
510
+ expect(config_document.set_value("a.b.c", "{\n d:e\n}").render).to eq("a {\n b {\n c : {\n d:e\n }\n }\n}")
511
+ end
512
+
513
+ it "should properly indent a multi-line value when replacing a single-line value in an object with multiple keys" do
514
+ orig_text = "a {\n b {\n f : 10\n c : 22\n }\n}"
515
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
516
+ expect(config_document.set_value("a.b.c", "{\n d:e\n}").render).to eq("a {\n b {\n f : 10\n c : {\n d:e\n }\n }\n}")
517
+ end
518
+ end
519
+
520
+ context "config document indentation value with include" do
521
+ it "should indent an include node" do
522
+ orig_text = "a {\n b {\n c : 22\n }\n}"
523
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
524
+ expect(config_document.set_value("a.b.d", "{\n include \"foo\"\n e:f\n}").render
525
+ ).to eq("a {\n b {\n c : 22\n d : {\n include \"foo\"\n e:f\n }\n }\n}")
526
+ end
527
+ end
528
+
529
+ context "config document indentation based on include node" do
530
+ it "should indent properly when only an include node is present in the object in which the value is inserted" do
531
+ orig_text = "a : b\n include \"foo\"\n"
532
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
533
+ expect(config_document.set_value("c", "d").render).to eq("a : b\n include \"foo\"\n c : d\n")
534
+ end
535
+ end
536
+
537
+ context "insertion into an empty document" do
538
+ it "should successfully insert a value into an empty document" do
539
+ orig_text = ""
540
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
541
+ expect(config_document.set_value("a", "1").render).to eq("a : 1")
542
+ end
543
+
544
+ it "should successfully insert a multi-line object into an empty document" do
545
+ orig_text = ""
546
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
547
+ expect(config_document.set_value("a.b", "1").render).to eq("a : {\n b : 1\n}")
548
+ end
549
+
550
+ it "should successfully insert a hash into an empty document" do
551
+ orig_text = ""
552
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
553
+ map_val = ConfigValueFactory.from_any_ref({"a" => 1, "b" => 2})
554
+
555
+ expect(config_document.set_config_value("a", map_val).render).to eq("a : {\n \"a\" : 1,\n \"b\" : 2\n}")
556
+ end
557
+
558
+ it "should successfully insert an array into an empty document" do
559
+ orig_text = ""
560
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
561
+ array_val = ConfigValueFactory.from_any_ref([1,2])
562
+
563
+ expect(config_document.set_config_value("a", array_val).render).to eq("a : [\n 1,\n 2\n]")
564
+ end
565
+ end
566
+
567
+ context "can insert a map parsed with ConfigValueFactory" do
568
+ it "should successfully insert a map into a document" do
569
+ orig_text = "{ a : b }"
570
+ config_document = ConfigDocumentFactory.parse_string(orig_text)
571
+
572
+ map = ConfigValueFactory.from_any_ref({"a" => 1, "b" => 2})
573
+ expect(config_document.set_config_value("a", map).render).to eq("{ a : {\n \"a\" : 1,\n \"b\" : 2\n } }")
574
+ end
575
+ end
576
+ end