hocon 0.9.5 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -2
  3. data/README.md +22 -10
  4. data/lib/hocon.rb +9 -3
  5. data/lib/hocon/config_factory.rb +4 -0
  6. data/lib/hocon/config_value_factory.rb +13 -2
  7. data/lib/hocon/impl/config_reference.rb +5 -2
  8. data/lib/hocon/impl/simple_config_origin.rb +1 -1
  9. data/spec/fixtures/parse_render/example1/input.conf +21 -0
  10. data/spec/fixtures/parse_render/example1/output.conf +26 -0
  11. data/spec/fixtures/parse_render/example1/output_nocomments.conf +17 -0
  12. data/spec/fixtures/parse_render/example2/input.conf +10 -0
  13. data/spec/fixtures/parse_render/example2/output.conf +17 -0
  14. data/spec/fixtures/parse_render/example2/output_nocomments.conf +17 -0
  15. data/spec/fixtures/parse_render/example3/input.conf +2 -0
  16. data/spec/fixtures/parse_render/example3/output.conf +2 -0
  17. data/spec/fixtures/parse_render/example4/input.json +6 -0
  18. data/spec/fixtures/parse_render/example4/output.conf +6 -0
  19. data/spec/fixtures/test_utils/resources/bom.conf +2 -0
  20. data/spec/fixtures/test_utils/resources/cycle.conf +1 -0
  21. data/spec/fixtures/test_utils/resources/file-include.conf +5 -0
  22. data/spec/fixtures/test_utils/resources/include-from-list.conf +4 -0
  23. data/spec/fixtures/test_utils/resources/subdir/bar.conf +1 -0
  24. data/spec/fixtures/test_utils/resources/subdir/baz.conf +1 -0
  25. data/spec/fixtures/test_utils/resources/subdir/foo.conf +5 -0
  26. data/spec/fixtures/test_utils/resources/test01.conf +80 -0
  27. data/spec/fixtures/test_utils/resources/test01.json +4 -0
  28. data/spec/fixtures/test_utils/resources/test03.conf +36 -0
  29. data/spec/spec_helper.rb +43 -0
  30. data/spec/test_utils.rb +757 -0
  31. data/spec/unit/typesafe/config/concatenation_spec.rb +417 -0
  32. data/spec/unit/typesafe/config/conf_parser_spec.rb +822 -0
  33. data/spec/unit/typesafe/config/config_document_parser_spec.rb +494 -0
  34. data/spec/unit/typesafe/config/config_document_spec.rb +576 -0
  35. data/spec/unit/typesafe/config/config_factory_spec.rb +120 -0
  36. data/spec/unit/typesafe/config/config_node_spec.rb +552 -0
  37. data/spec/unit/typesafe/config/config_value_factory_spec.rb +85 -0
  38. data/spec/unit/typesafe/config/config_value_spec.rb +935 -0
  39. data/spec/unit/typesafe/config/hocon_spec.rb +54 -0
  40. data/spec/unit/typesafe/config/path_spec.rb +261 -0
  41. data/spec/unit/typesafe/config/public_api_spec.rb +520 -0
  42. data/spec/unit/typesafe/config/simple_config_spec.rb +112 -0
  43. data/spec/unit/typesafe/config/token_spec.rb +188 -0
  44. data/spec/unit/typesafe/config/tokenizer_spec.rb +801 -0
  45. metadata +39 -3
@@ -0,0 +1,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