Skip to content

Commit d945735

Browse files
Config.Root should contain fallback values (#181)
* Fixed fallbacks handling * Refactored Config class to reuse Root property * Added configuration spec for complex objects with fallback Co-authored-by: Aaron Stannard <[email protected]>
1 parent 4216028 commit d945735

File tree

2 files changed

+79
-29
lines changed

2 files changed

+79
-29
lines changed

src/Hocon.Configuration.Test/ConfigurationSpec.cs

+47
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,47 @@ public void Config_Empty_is_Empty()
395395
ConfigurationFactory.Empty.IsEmpty.Should().BeTrue();
396396
}
397397

398+
[Fact]
399+
public void HoconValue_GetObject_should_use_fallback_values()
400+
{
401+
var config1 = ConfigurationFactory.ParseString("a = 5");
402+
var config2 = ConfigurationFactory.ParseString("b = 3");
403+
var config = config1.WithFallback(config2);
404+
var rootObject = config.Root.GetObject();
405+
rootObject.ContainsKey("a").Should().BeTrue();
406+
rootObject["a"].Raw.Should().Be("5");
407+
rootObject.ContainsKey("b").Should().BeTrue();
408+
rootObject["b"].Raw.Should().Be("3");
409+
}
410+
411+
[Fact]
412+
public void HoconValue_GetObject_should_use_fallback_values_with_complex_objects()
413+
{
414+
var config1 = ConfigurationFactory.ParseString(@"
415+
akka.actor.deployment {
416+
/worker1 {
417+
router = round-robin-group1
418+
routees.paths = [""/user/testroutes/1""]
419+
}
420+
}");
421+
var config2 = ConfigurationFactory.ParseString(@"
422+
akka.actor.deployment {
423+
/worker2 {
424+
router = round-robin-group2
425+
routees.paths = [""/user/testroutes/2""]
426+
}
427+
}");
428+
var configWithFallback = config1.WithFallback(config2);
429+
430+
var config = configWithFallback.GetConfig("akka.actor.deployment");
431+
var rootObj = config.Root.GetObject();
432+
rootObj.Unwrapped.Should().ContainKeys("/worker1", "/worker2");
433+
rootObj["/worker1.router"].Raw.Should().Be("round-robin-group1");
434+
rootObj["/worker1.router"].Raw.Should().Be("round-robin-group1");
435+
rootObj["/worker1.routees.paths"].Value[0].GetArray()[0].Raw.Should().Be(@"""/user/testroutes/1""");
436+
rootObj["/worker2.routees.paths"].Value[0].GetArray()[0].Raw.Should().Be(@"""/user/testroutes/2""");
437+
}
438+
398439

399440
#if !NETCORE
400441
[Fact]
@@ -427,9 +468,15 @@ public void ShouldSerializeFallbackValues()
427468
}");
428469

429470
var c = a.WithFallback(b);
471+
430472
c.GetInt("akka.other-key").Should().Be(42, "Fallback value should exist as data");
431473
c.ToString().Should().NotContain("other-key", "Fallback values are ignored by default");
432474
c.ToString(true).Should().Contain("other-key", "Fallback values should be displayed when requested");
475+
476+
c.GetString("akka.some-key").Should().Be("value", "Original value should remain");
477+
c.ToString().Should().Contain("some-key", "Original values are shown by default");
478+
c.ToString(true).Should().Contain("some-key", "Original values should be displayed always");
479+
433480
}
434481

435482
/// <summary>

src/Hocon.Configuration/Config.cs

+32-29
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,29 @@ public Config(HoconRoot source, Config fallback) : base(source?.Value,
5959
/// <summary>
6060
/// The root node of this configuration section
6161
/// </summary>
62-
public virtual HoconValue Root => Value;
62+
public virtual HoconValue Root
63+
{
64+
get
65+
{
66+
var elements = Value.ToList();
67+
var config = this;
68+
while (config.Fallback != null)
69+
{
70+
config = config.Fallback;
71+
if (config.Value != null)
72+
elements.AddRange(config.Value);
73+
}
74+
75+
var aggregated = new HoconValue(null);
76+
elements.Reverse();
77+
foreach (var element in elements)
78+
{
79+
aggregated.Add(element);
80+
}
81+
82+
return aggregated;
83+
}
84+
}
6385

6486
/// <summary>
6587
/// Returns string representation of <see cref="Config" />, allowing to include fallback values
@@ -69,12 +91,8 @@ public string ToString(bool useFallbackValues)
6991
{
7092
if (!useFallbackValues)
7193
return base.ToString();
72-
73-
var config = this;
74-
while (config.Fallback != null)
75-
config = config.Fallback;
76-
77-
return config.ToString();
94+
95+
return Root.ToString();
7896
}
7997

8098
/// <summary>
@@ -93,20 +111,17 @@ protected Config Copy()
93111

94112
protected override HoconValue GetNode(HoconPath path, bool throwIfNotFound = false)
95113
{
96-
HoconValue result;
97114
try
98115
{
99-
result = Root.GetObject().GetValue(path);
116+
return Root.GetObject().GetValue(path);
100117
}
101118
catch
102119
{
103120
if (throwIfNotFound)
104121
throw;
105122

106-
result = Fallback?.GetNode(path);
123+
return null;
107124
}
108-
109-
return result;
110125
}
111126

112127
/// <summary>
@@ -123,12 +138,6 @@ public virtual Config GetConfig(string path)
123138
public virtual Config GetConfig(HoconPath path)
124139
{
125140
var value = GetNode(path);
126-
if (Fallback != null)
127-
{
128-
var f = Fallback.GetConfig(path);
129-
return value == null ? f : new Config(new HoconRoot(value)).WithFallback(f);
130-
}
131-
132141
return value == null ? null : new Config(new HoconRoot(value));
133142
}
134143

@@ -188,19 +197,13 @@ public static implicit operator Config(string str)
188197
public override IEnumerable<KeyValuePair<string, HoconField>> AsEnumerable()
189198
{
190199
var used = new HashSet<string>();
191-
var current = this;
192-
while (current != null)
200+
foreach (var kvp in Root.GetObject())
193201
{
194-
foreach (var kvp in current.Root.GetObject())
195-
{
196-
if (used.Contains(kvp.Key))
197-
continue;
198-
199-
yield return kvp;
200-
used.Add(kvp.Key);
201-
}
202+
if (used.Contains(kvp.Key))
203+
continue;
202204

203-
current = current.Fallback;
205+
yield return kvp;
206+
used.Add(kvp.Key);
204207
}
205208
}
206209
}

0 commit comments

Comments
 (0)