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,120 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'hocon/config_factory'
5
+ require 'hocon/config_render_options'
6
+ require 'hocon/config_error'
7
+
8
+ def get_comment_config_hash(config_string)
9
+ split_config_string = config_string.split("\n")
10
+ r = Regexp.new('^\s*#')
11
+
12
+ previous_string_comment = false
13
+ hash = {}
14
+ comment_list = []
15
+
16
+ split_config_string.each do |s|
17
+ if r.match(s)
18
+ comment_list << s
19
+ previous_string_comment = true
20
+ else
21
+ if previous_string_comment
22
+ hash[s] = comment_list
23
+ comment_list = []
24
+ end
25
+ previous_string_comment = false
26
+ end
27
+ end
28
+ return hash
29
+ end
30
+
31
+ describe Hocon::ConfigFactory do
32
+ let(:render_options) { Hocon::ConfigRenderOptions.defaults }
33
+
34
+ before do
35
+ render_options.origin_comments = false
36
+ render_options.json = false
37
+ end
38
+
39
+ shared_examples_for "config_factory_parsing" do
40
+ let(:input_file) { "#{FIXTURE_DIR}/parse_render/#{example[:name]}/input#{extension}" }
41
+ let(:output_file) { "#{FIXTURE_DIR}/parse_render/#{example[:name]}/output.conf" }
42
+ let(:expected) { example[:hash] }
43
+ let(:reparsed) { Hocon::ConfigFactory.parse_file("#{output_file}") }
44
+ let(:output) { File.read("#{output_file}") }
45
+
46
+ it "should make the config data available as a map" do
47
+ expect(conf.root.unwrapped).to eq(expected)
48
+ end
49
+
50
+ it "should render the config data to a string with comments intact" do
51
+ rendered_conf = conf.root.render(render_options)
52
+ rendered_conf_comment_hash = get_comment_config_hash(rendered_conf)
53
+ output_comment_hash = get_comment_config_hash(output)
54
+
55
+ expect(rendered_conf_comment_hash).to eq(output_comment_hash)
56
+ end
57
+
58
+ it "should generate the same conf data via re-parsing the rendered output" do
59
+ expect(reparsed.root.unwrapped).to eq(expected)
60
+ end
61
+ end
62
+
63
+ context "example1" do
64
+ let(:example) { EXAMPLE1 }
65
+ let (:extension) { ".conf" }
66
+
67
+ context "parsing a HOCON string" do
68
+ let(:string) { File.open(input_file).read }
69
+ let(:conf) { Hocon::ConfigFactory.parse_string(string) }
70
+ include_examples "config_factory_parsing"
71
+ end
72
+
73
+ context "parsing a .conf file" do
74
+ let(:conf) { Hocon::ConfigFactory.parse_file(input_file) }
75
+ include_examples "config_factory_parsing"
76
+ end
77
+ end
78
+
79
+ context "example2" do
80
+ let(:example) { EXAMPLE2 }
81
+ let (:extension) { ".conf" }
82
+
83
+ context "parsing a HOCON string" do
84
+ let(:string) { File.open(input_file).read }
85
+ let(:conf) { Hocon::ConfigFactory.parse_string(string) }
86
+ include_examples "config_factory_parsing"
87
+ end
88
+
89
+ context "parsing a .conf file" do
90
+ let(:conf) { Hocon::ConfigFactory.parse_file(input_file) }
91
+ include_examples "config_factory_parsing"
92
+ end
93
+ end
94
+
95
+ context "example3" do
96
+ let (:example) { EXAMPLE3 }
97
+ let (:extension) { ".conf" }
98
+
99
+ context "loading a HOCON file with substitutions" do
100
+ let(:conf) { Hocon::ConfigFactory.load_file(input_file) }
101
+ include_examples "config_factory_parsing"
102
+ end
103
+ end
104
+
105
+ context "example4" do
106
+ let(:example) { EXAMPLE4 }
107
+ let (:extension) { ".json" }
108
+
109
+ context "parsing a .json file" do
110
+ let (:conf) { Hocon::ConfigFactory.parse_file(input_file) }
111
+ include_examples "config_factory_parsing"
112
+ end
113
+ end
114
+
115
+ context "example5" do
116
+ it "should raise a ConfigParseError when given an invalid .conf file" do
117
+ expect{Hocon::ConfigFactory.parse_string("abcdefg")}.to raise_error(Hocon::ConfigError::ConfigParseError)
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,552 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'hocon'
5
+ require 'test_utils'
6
+
7
+ describe Hocon::Parser::ConfigNode do
8
+ Tokens = Hocon::Impl::Tokens
9
+
10
+ shared_examples_for "single token node test" do
11
+ it "should render the node with the text of the token" do
12
+ node = TestUtils.config_node_single_token(token)
13
+ expect(node.render).to eq(token.token_text)
14
+ end
15
+ end
16
+
17
+ shared_examples_for "key node test" do
18
+ it "should render the node with the text of the path" do
19
+ node = TestUtils.config_node_key(path)
20
+ expect(path).to eq(node.render)
21
+ end
22
+ end
23
+
24
+ shared_examples_for "simple value node test" do
25
+ it "should render the original token text" do
26
+ node = TestUtils.config_node_simple_value(token)
27
+ expect(node.render).to eq(token.token_text)
28
+ end
29
+ end
30
+
31
+ shared_examples_for "field node test" do
32
+ it "should properly replace the value of a field node" do
33
+ key_val_node = TestUtils.node_key_value_pair(key, value)
34
+ expect(key_val_node.render).to eq("#{key.render} : #{value.render}")
35
+ expect(key_val_node.path.render).to eq(key.render)
36
+ expect(key_val_node.value.render).to eq(value.render)
37
+
38
+ new_key_val_node = key_val_node.replace_value(new_value)
39
+ expect(new_key_val_node.render).to eq("#{key.render} : #{new_value.render}")
40
+ expect(new_key_val_node.value.render).to eq(new_value.render)
41
+ end
42
+ end
43
+
44
+ shared_examples_for "top level value replace test" do
45
+ it "should replace a value in a ConfigNodeObject" do
46
+ complex_node_children = [TestUtils.node_open_brace,
47
+ TestUtils.node_key_value_pair(TestUtils.config_node_key(key), value),
48
+ TestUtils.node_close_brace]
49
+ complex_node = TestUtils.config_node_object(complex_node_children)
50
+ new_node = complex_node.set_value_on_path(key, new_value)
51
+ orig_text = "{#{key} : #{value.render}}"
52
+ final_text = "{#{key} : #{new_value.render}}"
53
+
54
+ expect(complex_node.render).to eq(orig_text)
55
+ expect(new_node.render).to eq(final_text)
56
+ end
57
+ end
58
+
59
+ shared_examples_for "replace duplicates test" do
60
+ it "should remove duplicates of a key when setting a value" do
61
+ key = TestUtils.config_node_key('foo')
62
+ key_val_pair_1 = TestUtils.node_key_value_pair(key, value1)
63
+ key_val_pair_2 = TestUtils.node_key_value_pair(key, value2)
64
+ key_val_pair_3 = TestUtils.node_key_value_pair(key, value3)
65
+ complex_node = TestUtils.config_node_object([key_val_pair_1, key_val_pair_2, key_val_pair_3])
66
+ orig_text = "#{key_val_pair_1.render}#{key_val_pair_2.render}#{key_val_pair_3.render}"
67
+ final_text = "#{key.render} : 15"
68
+
69
+ expect(complex_node.render).to eq(orig_text)
70
+ expect(complex_node.set_value_on_path("foo", TestUtils.node_int(15)).render).to eq(final_text)
71
+ end
72
+ end
73
+
74
+ shared_examples_for "non existent path test" do
75
+ it "should properly add a key/value pair if the key does not exist in the object" do
76
+ node = TestUtils.config_node_object([TestUtils.node_key_value_pair(TestUtils.config_node_key("bar"), TestUtils.node_int(15))])
77
+ expect(node.render).to eq('bar : 15')
78
+ new_node = node.set_value_on_path('foo', value)
79
+ final_text = "bar : 15, foo : #{value.render}"
80
+ expect(new_node.render).to eq(final_text)
81
+ end
82
+ end
83
+
84
+ ########################
85
+ # ConfigNodeSingleToken
86
+ ########################
87
+ context "create basic config node" do
88
+ # Ensure a ConfigNodeSingleToken can handle all its required token types
89
+ context "start of file" do
90
+ let(:token) { Tokens::START }
91
+ include_examples "single token node test"
92
+ end
93
+
94
+ context "end of file" do
95
+ let(:token) { Tokens::EOF }
96
+ include_examples "single token node test"
97
+ end
98
+
99
+ context "{" do
100
+ let (:token) { Tokens::OPEN_CURLY }
101
+ include_examples "single token node test"
102
+ end
103
+
104
+ context "}" do
105
+ let (:token) { Tokens::CLOSE_CURLY }
106
+ include_examples "single token node test"
107
+ end
108
+
109
+ context "[" do
110
+ let (:token) { Tokens::OPEN_SQUARE }
111
+ include_examples "single token node test"
112
+ end
113
+
114
+ context "]" do
115
+ let (:token) { Tokens::CLOSE_SQUARE }
116
+ include_examples "single token node test"
117
+ end
118
+
119
+ context "," do
120
+ let (:token) { Tokens::COMMA }
121
+ include_examples "single token node test"
122
+ end
123
+
124
+ context "=" do
125
+ let (:token) { Tokens::EQUALS }
126
+ include_examples "single token node test"
127
+ end
128
+
129
+ context ":" do
130
+ let (:token) { Tokens::COLON }
131
+ include_examples "single token node test"
132
+ end
133
+
134
+ context "+=" do
135
+ let (:token) { Tokens::PLUS_EQUALS }
136
+ include_examples "single token node test"
137
+ end
138
+
139
+ context "unquoted text" do
140
+ let (:token) { TestUtils.token_unquoted(' ') }
141
+ include_examples "single token node test"
142
+ end
143
+
144
+ context "ignored whitespace" do
145
+ let (:token) { TestUtils.token_whitespace(' ') }
146
+ include_examples "single token node test"
147
+ end
148
+
149
+ context '\n' do
150
+ let (:token) { TestUtils.token_line(1) }
151
+ include_examples "single token node test"
152
+ end
153
+
154
+ context "double slash comment" do
155
+ let (:token) { TestUtils.token_comment_double_slash(" this is a double slash comment ") }
156
+ include_examples "single token node test"
157
+ end
158
+
159
+ context "hash comment" do
160
+ let (:token) { TestUtils.token_comment_hash(" this is a hash comment ") }
161
+ include_examples "single token node test"
162
+ end
163
+ end
164
+
165
+ ####################
166
+ # ConfigNodeSetting
167
+ ####################
168
+ context "create config node setting" do
169
+ # Ensure a ConfigNodeSetting can handle the normal key types
170
+ context "unquoted key" do
171
+ let (:path) { "foo" }
172
+ include_examples "key node test"
173
+ end
174
+
175
+ context "quoted_key" do
176
+ let (:path) { "\"Hello I am a key how are you today\"" }
177
+ include_examples "key node test"
178
+ end
179
+ end
180
+
181
+ context "path node subpath" do
182
+ it "should produce correct subpaths of path nodes with subpath method" do
183
+ orig_path = 'a.b.c."@$%#@!@#$"."".1234.5678'
184
+ path_node = TestUtils.config_node_key(orig_path)
185
+
186
+ expect(path_node.render).to eq(orig_path)
187
+ expect(path_node.sub_path(2).render).to eq('c."@$%#@!@#$"."".1234.5678')
188
+ expect(path_node.sub_path(6).render).to eq('5678')
189
+ end
190
+ end
191
+
192
+ ########################
193
+ # ConfigNodeSimpleValue
194
+ ########################
195
+ context "create config node simple value" do
196
+ context "integer" do
197
+ let (:token) { TestUtils.token_int(10) }
198
+ include_examples "simple value node test"
199
+ end
200
+
201
+ context "double" do
202
+ let (:token) { TestUtils.token_double(3.14159) }
203
+ include_examples "simple value node test"
204
+ end
205
+
206
+ context "false" do
207
+ let (:token) { TestUtils.token_false }
208
+ include_examples "simple value node test"
209
+ end
210
+
211
+ context "true" do
212
+ let (:token) { TestUtils.token_true }
213
+ include_examples "simple value node test"
214
+ end
215
+
216
+ context "null" do
217
+ let (:token) { TestUtils.token_null }
218
+ include_examples "simple value node test"
219
+ end
220
+
221
+ context "quoted text" do
222
+ let (:token) { TestUtils.token_string("Hello my name is string") }
223
+ include_examples "simple value node test"
224
+ end
225
+
226
+ context "unquoted text" do
227
+ let (:token) { TestUtils.token_unquoted("mynameisunquotedstring") }
228
+ include_examples "simple value node test"
229
+ end
230
+
231
+ context "key substitution" do
232
+ let (:token) { TestUtils.token_key_substitution("c.d") }
233
+ include_examples "simple value node test"
234
+ end
235
+
236
+ context "optional substitution" do
237
+ let (:token) { TestUtils.token_optional_substitution(TestUtils.token_unquoted("x.y")) }
238
+ include_examples "simple value node test"
239
+ end
240
+
241
+ context "substitution" do
242
+ let (:token) { TestUtils.token_substitution(TestUtils.token_unquoted("a.b")) }
243
+ include_examples "simple value node test"
244
+ end
245
+ end
246
+
247
+ ####################
248
+ # ConfigNodeField
249
+ ####################
250
+ context "create ConfigNodeField" do
251
+ let (:key) { TestUtils.config_node_key('"abc"') }
252
+ let (:value) { TestUtils.node_int(123) }
253
+
254
+ context "supports quoted keys" do
255
+ let (:new_value) { TestUtils.node_int(245) }
256
+ include_examples "field node test"
257
+ end
258
+
259
+ context "supports unquoted keys" do
260
+ let (:key) { TestUtils.config_node_key('abc') }
261
+ let (:new_value) { TestUtils.node_int(245) }
262
+ include_examples "field node test"
263
+ end
264
+
265
+ context "can replace a simple value with a different type of simple value" do
266
+ let (:new_value) { TestUtils.node_string('I am a string') }
267
+ include_examples "field node test"
268
+ end
269
+
270
+ context "can replace a simple value with a complex value" do
271
+ let (:new_value) { TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace]) }
272
+ include_examples "field node test"
273
+ end
274
+ end
275
+
276
+ ####################
277
+ # Node Replacement
278
+ ####################
279
+ context "replace nodes" do
280
+ let (:key) { "foo" }
281
+ array = TestUtils.config_node_array([TestUtils.node_open_bracket, TestUtils.node_int(10), TestUtils.node_space, TestUtils.node_comma,
282
+ TestUtils.node_space, TestUtils.node_int(15), TestUtils.node_close_bracket])
283
+ nested_map = TestUtils.config_node_object([TestUtils.node_open_brace,
284
+ TestUtils.node_key_value_pair(TestUtils.config_node_key("abc"),
285
+ TestUtils.config_node_simple_value(TestUtils.token_string("a string"))),
286
+ TestUtils.node_close_brace])
287
+
288
+ context "replace an integer with an integer" do
289
+ let (:value) { TestUtils.node_int(10) }
290
+ let (:new_value) { TestUtils.node_int(15) }
291
+ include_examples "top level value replace test"
292
+ end
293
+
294
+ context "replace a double with an integer" do
295
+ let (:value) { TestUtils.node_double(3.14159) }
296
+ let (:new_value) { TestUtils.node_int(10000) }
297
+ include_examples "top level value replace test"
298
+ end
299
+
300
+ context "replace false with true" do
301
+ let (:value) { TestUtils.node_false }
302
+ let (:new_value) { TestUtils.node_true }
303
+ include_examples "top level value replace test"
304
+ end
305
+
306
+ context "replace true with null" do
307
+ let (:value) { TestUtils.node_true }
308
+ let (:new_value) { TestUtils.node_null }
309
+ include_examples "top level value replace test"
310
+ end
311
+
312
+ context "replace null with a string" do
313
+ let (:value) { TestUtils.node_null }
314
+ let (:new_value) { TestUtils.node_string("Hello my name is string") }
315
+ include_examples "top level value replace test"
316
+ end
317
+
318
+ context "replace a string with unquoted text" do
319
+ let (:value) { TestUtils.node_string("Hello my name is string") }
320
+ let (:new_value) { TestUtils.node_unquoted_text("mynameisunquotedstring") }
321
+ include_examples "top level value replace test"
322
+ end
323
+
324
+ context "replace unquoted text with a key substitution" do
325
+ let (:value) { TestUtils.node_unquoted_text("mynameisunquotedstring") }
326
+ let (:new_value) { TestUtils.node_key_substitution("c.d") }
327
+ include_examples "top level value replace test"
328
+ end
329
+
330
+ context "replace int with an optional substitution" do
331
+ let (:value) { TestUtils.node_int(10) }
332
+ let (:new_value) { TestUtils.node_optional_substitution(TestUtils.token_unquoted("x.y")) }
333
+ include_examples "top level value replace test"
334
+ end
335
+
336
+ context "replace int with a substitution" do
337
+ let (:value) { TestUtils.node_int(10) }
338
+ let (:new_value) { TestUtils.node_substitution(TestUtils.token_unquoted("a.b")) }
339
+ include_examples "top level value replace test"
340
+ end
341
+
342
+ context "replace substitution with an int" do
343
+ let (:value) { TestUtils.node_substitution(TestUtils.token_unquoted("a.b")) }
344
+ let (:new_value) { TestUtils.node_int(10) }
345
+ include_examples "top level value replace test"
346
+ end
347
+
348
+ context "ensure arrays can be replaced" do
349
+ context "can replace a simple value with an array" do
350
+ let (:value) { TestUtils.node_int(10) }
351
+ let (:new_value) { array }
352
+ include_examples "top level value replace test"
353
+ end
354
+
355
+ context "can replace an array with a simple value" do
356
+ let (:value) { array }
357
+ let (:new_value) { TestUtils.node_int(10) }
358
+ include_examples "top level value replace test"
359
+ end
360
+
361
+ context "can replace an array with another complex value" do
362
+ let (:value) { array }
363
+ let (:new_value) { TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace])}
364
+ include_examples "top level value replace test"
365
+ end
366
+ end
367
+
368
+ context "ensure objects can be replaced" do
369
+ context "can replace an object with a simple value" do
370
+ let (:value) { nested_map }
371
+ let (:new_value) { TestUtils.node_int(10) }
372
+ include_examples "top level value replace test"
373
+ end
374
+
375
+ context "can replace a simple value with an object" do
376
+ let (:value) { TestUtils.node_int(10) }
377
+ let (:new_value) { nested_map }
378
+ include_examples "top level value replace test"
379
+ end
380
+
381
+ context "can replace an array with an object" do
382
+ let (:value) { array }
383
+ let (:new_value) { nested_map }
384
+ include_examples "top level value replace test"
385
+ end
386
+
387
+ context "can replace an object with an array" do
388
+ let (:value) { nested_map }
389
+ let (:new_value) { array }
390
+ include_examples "top level value replace test"
391
+ end
392
+
393
+ context "can replace an object with an empty object" do
394
+ let (:value) { nested_map }
395
+ let (:new_value) { TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace]) }
396
+ include_examples "top level value replace test"
397
+ end
398
+ end
399
+
400
+ context "ensure concatenations can be replaced" do
401
+ concatenation = TestUtils.config_node_concatenation([TestUtils.node_int(10), TestUtils.node_space, TestUtils.node_string("Hello")])
402
+
403
+ context "can replace a concatenation with a simple value" do
404
+ let (:value) { concatenation }
405
+ let (:new_value) { TestUtils.node_int(12) }
406
+ include_examples "top level value replace test"
407
+ end
408
+
409
+ context "can replace a simple value with a concatenation" do
410
+ let (:value) { TestUtils.node_int(12) }
411
+ let (:new_value) { concatenation }
412
+ include_examples "top level value replace test"
413
+ end
414
+
415
+ context "can replace an object with a concatenation" do
416
+ let (:value) { nested_map }
417
+ let (:new_value) { concatenation }
418
+ include_examples "top level value replace test"
419
+ end
420
+
421
+ context "can replace a concatenation with an object" do
422
+ let (:value) { concatenation }
423
+ let (:new_value) { nested_map }
424
+ include_examples "top level value replace test"
425
+ end
426
+
427
+ context "can replace an array with a concatenation" do
428
+ let (:value) { array }
429
+ let (:new_value) { concatenation }
430
+ include_examples "top level value replace test"
431
+ end
432
+
433
+ context "can replace a concatenation with an array" do
434
+ let (:value) { concatenation }
435
+ let (:new_value) { array }
436
+ include_examples "top level value replace test"
437
+ end
438
+ end
439
+
440
+ context 'ensure a key with format "a.b" will be properly replaced' do
441
+ let (:key) { 'foo.bar' }
442
+ let (:value) { TestUtils.node_int(10) }
443
+ let (:new_value) { nested_map }
444
+ include_examples "top level value replace test"
445
+ end
446
+ end
447
+
448
+ ####################
449
+ # Duplicate Removal
450
+ ####################
451
+ context "remove duplicates" do
452
+ empty_map_node = TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_close_brace])
453
+ empty_array_node = TestUtils.config_node_array([TestUtils.node_open_bracket, TestUtils.node_close_bracket])
454
+
455
+ context "duplicates containing simple values will all be removed" do
456
+ let (:value1) { TestUtils.node_int(10) }
457
+ let (:value2) { TestUtils.node_true }
458
+ let (:value3) { TestUtils.node_null }
459
+ include_examples "replace duplicates test"
460
+ end
461
+
462
+ context "duplicates containing objects will be removed" do
463
+ let (:value1) { empty_map_node }
464
+ let (:value2) { empty_map_node }
465
+ let (:value3) { empty_map_node }
466
+ include_examples "replace duplicates test"
467
+ end
468
+
469
+ context "duplicates containing arrays will be removed" do
470
+ let (:value1) { empty_array_node }
471
+ let (:value2) { empty_array_node }
472
+ let (:value3) { empty_array_node }
473
+ include_examples "replace duplicates test"
474
+ end
475
+
476
+ context "duplicates containing a mix of value types will be removed" do
477
+ let (:value1) { TestUtils.node_int(10) }
478
+ let (:value2) { empty_map_node }
479
+ let (:value3) { empty_array_node }
480
+ include_examples "replace duplicates test"
481
+ end
482
+ end
483
+
484
+ #################################
485
+ # Addition of non-existent paths
486
+ #################################
487
+ context "add non existent paths" do
488
+ context "adding an integer" do
489
+ let (:value) { TestUtils.node_int(10) }
490
+ include_examples "non existent path test"
491
+ end
492
+
493
+ context "adding an array" do
494
+ let (:value) { TestUtils.config_node_array([TestUtils.node_open_bracket, TestUtils.node_int(15), TestUtils.node_close_bracket]) }
495
+ include_examples "non existent path test"
496
+ end
497
+
498
+ context "adding an object" do
499
+ let (:value) { TestUtils.config_node_object([TestUtils.node_open_brace,
500
+ TestUtils.node_key_value_pair(TestUtils.config_node_key('foo'),
501
+ TestUtils.node_double(3.14)),
502
+ TestUtils.node_close_brace]) }
503
+ include_examples "non existent path test"
504
+ end
505
+ end
506
+
507
+ #################################
508
+ # Replacement of nested nodes
509
+ #################################
510
+ context "replace nested nodes" do
511
+ orig_text = "foo : bar\nbaz : {\n\t\"abc.def\" : 123\n\t//This is a comment about the below setting\n\n\tabc : {\n\t\t" +
512
+ "def : \"this is a string\"\n\t\tghi : ${\"a.b\"}\n\t}\n}\nbaz.abc.ghi : 52\nbaz.abc.ghi : 53\n}"
513
+ lowest_level_map = TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_line(6), TestUtils.node_whitespace("\t\t"),
514
+ TestUtils.node_key_value_pair(TestUtils.config_node_key("def"), TestUtils.config_node_simple_value(TestUtils.token_string("this is a string"))),
515
+ TestUtils.node_line(7), TestUtils.node_whitespace("\t\t"),
516
+ TestUtils.node_key_value_pair(TestUtils.config_node_key("ghi"), TestUtils.config_node_simple_value(TestUtils.token_key_substitution("a.b"))),
517
+ TestUtils.node_line(8), TestUtils.node_whitespace("\t"), TestUtils.node_close_brace])
518
+ higher_level_map = TestUtils.config_node_object([TestUtils.node_open_brace, TestUtils.node_line(2), TestUtils.node_whitespace("\t"),
519
+ TestUtils.node_key_value_pair(TestUtils.config_node_key('"abc.def"'), TestUtils.config_node_simple_value(TestUtils.token_int(123))),
520
+ TestUtils.node_line(3), TestUtils.node_whitespace("\t"), TestUtils.node_comment_double_slash("This is a comment about the below setting"),
521
+ TestUtils.node_line(4), TestUtils.node_line(5), TestUtils.node_whitespace("\t"),
522
+ TestUtils.node_key_value_pair(TestUtils.config_node_key("abc"), lowest_level_map), TestUtils.node_line(9), TestUtils.node_close_brace])
523
+ orig_node = TestUtils.config_node_object([TestUtils.node_key_value_pair(TestUtils.config_node_key("foo"), TestUtils.config_node_simple_value(TestUtils.token_unquoted("bar"))),
524
+ TestUtils.node_line(1), TestUtils.node_key_value_pair(TestUtils.config_node_key('baz'), higher_level_map), TestUtils.node_line(10),
525
+ TestUtils.node_key_value_pair(TestUtils.config_node_key('baz.abc.ghi'), TestUtils.config_node_simple_value(TestUtils.token_int(52))),
526
+ TestUtils.node_line(11),
527
+ TestUtils.node_key_value_pair(TestUtils.config_node_key('baz.abc.ghi'), TestUtils.config_node_simple_value(TestUtils.token_int(53))),
528
+ TestUtils.node_line(12), TestUtils.node_close_brace])
529
+ it "should properly render the original node" do
530
+ expect(orig_node.render).to eq(orig_text)
531
+ end
532
+
533
+ it "should properly replae values in the original node" do
534
+ final_text = "foo : bar\nbaz : {\n\t\"abc.def\" : true\n\t//This is a comment about the below setting\n\n\tabc : {\n\t\t" +
535
+ "def : false\n\t\t\n\t\t\"this.does.not.exist@@@+$#\" : {\n\t\t end : doesnotexist\n\t\t}\n\t}\n}\n\nbaz.abc.ghi : randomunquotedString\n}"
536
+
537
+ # Paths with quotes in the name are treated as a single Path, rather than multiple sub-paths
538
+ new_node = orig_node.set_value_on_path('baz."abc.def"', TestUtils.config_node_simple_value(TestUtils.token_true))
539
+ new_node = new_node.set_value_on_path('baz.abc.def', TestUtils.config_node_simple_value(TestUtils.token_false))
540
+
541
+ # Repeats are removed from nested maps
542
+ new_node = new_node.set_value_on_path('baz.abc.ghi', TestUtils.config_node_simple_value(TestUtils.token_unquoted('randomunquotedString')))
543
+
544
+ # Missing paths are added to the top level if they don't appear anywhere, including in nested maps
545
+ new_node = new_node.set_value_on_path('baz.abc."this.does.not.exist@@@+$#".end', TestUtils.config_node_simple_value(TestUtils.token_unquoted('doesnotexist')))
546
+
547
+ # The above operations cause the resultant map to be rendered properly
548
+ expect(new_node.render).to eq(final_text)
549
+ end
550
+ end
551
+
552
+ end