add Config.resolveWith()

This allows replacing substitutions with values which
are not merged into the Config.

Fixes #95
This commit is contained in:
Havoc Pennington 2014-01-03 11:02:45 -05:00
parent d7287c9e16
commit 247236c6f1
3 changed files with 79 additions and 7 deletions

View File

@ -184,6 +184,46 @@ public interface Config extends ConfigMergeable {
*/
boolean isResolved();
/**
* Like {@link Config#resolve()} except that substitution values are looked
* up in the given source, rather than in this instance. This is a
* special-purpose method which doesn't make sense to use in most cases;
* it's only needed if you're constructing some sort of app-specific custom
* approach to configuration. The more usual approach if you have a source
* of substitution values would be to merge that source into your config
* stack using {@link Config#withFallback} and then resolve.
* <p>
* Note that this method does NOT look in this instance for substitution
* values. If you want to do that, you could either merge this instance into
* your value source using {@link Config#withFallback}, or you could resolve
* multiple times with multiple sources (using
* {@link ConfigResolveOptions#setAllowUnresolved(boolean)} so the partial
* resolves don't fail).
*
* @param source
* configuration to pull values from
* @return an immutable object with substitutions resolved
* @throws ConfigException.UnresolvedSubstitution
* if any substitutions refer to paths which are not in the
* source
* @throws ConfigException
* some other config exception if there are other problems
*/
Config resolveWith(Config source);
/**
* Like {@link Config#resolveWith(Config)} but allows you to specify
* non-default options.
*
* @param source
* source configuration to pull values from
* @param options
* resolve options
* @return the resolved <code>Config</code> (may be only partially resolved
* if options are set to allow unresolved)
*/
Config resolveWith(Config source, ConfigResolveOptions options);
/**
* Validates this config against a reference config, throwing an exception
* if it is invalid. The purpose of this method is to "fail early" with a

View File

@ -57,7 +57,17 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
@Override
public SimpleConfig resolve(ConfigResolveOptions options) {
AbstractConfigValue resolved = ResolveContext.resolve(object, object, options);
return resolveWith(this, options);
}
@Override
public SimpleConfig resolveWith(Config source) {
return resolveWith(source, ConfigResolveOptions.defaults());
}
@Override
public SimpleConfig resolveWith(Config source, ConfigResolveOptions options) {
AbstractConfigValue resolved = ResolveContext.resolve(object, ((SimpleConfig) source).object, options);
if (resolved == object)
return this;
@ -65,7 +75,6 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
return new SimpleConfig((AbstractConfigObject) resolved);
}
@Override
public boolean hasPath(String pathExpression) {
Path path = Path.newPath(pathExpression);

View File

@ -1138,6 +1138,9 @@ class ConfigTest extends TestUtils {
}
// and the partially-resolved thing is not resolved
assertFalse("partially-resolved object is not resolved", allowedUnresolved.isResolved)
// scope "val resolved"
{
// and given the values for the resolve, we should be able to
val resolved = allowedUnresolved.withFallback(values).resolve()
for (kv <- Seq("a" -> 1, "b" -> 2, "c.x" -> 3, "c.y" -> 4)) {
@ -1145,4 +1148,24 @@ class ConfigTest extends TestUtils {
}
assertTrue("fully resolved object is resolved", resolved.isResolved)
}
// we should also be able to use resolveWith
{
val resolved = allowedUnresolved.resolveWith(values)
for (kv <- Seq("a" -> 1, "b" -> 2, "c.x" -> 3, "c.y" -> 4)) {
assertEquals(kv._2, resolved.getInt(kv._1))
}
assertTrue("fully resolved object is resolved", resolved.isResolved)
}
}
@Test
def resolveWithWorks(): Unit = {
// the a=42 is present here to be sure it gets ignored when we resolveWith
val unresolved = ConfigFactory.parseString("foo = ${a}, a = 42")
assertEquals(42, unresolved.resolve().getInt("foo"))
val source = ConfigFactory.parseString("a = 43")
val resolved = unresolved.resolveWith(source)
assertEquals(43, resolved.getInt("foo"))
}
}