config
config copied to clipboard
`include file()` is not relative to the file doing the including
When writing plugins for Bukkit, the plugin's data folder is located at "plugins/[plugin name]".
In order to make use of "include file()" I must enter in that entire path before hand such that it looks like this "foo = { include file("plugins/[plugin name]/bar.conf }. I want the include to implicitly assume the "plugins/<plugin name/" to keep my config files clean.
I have have a feeling It can be done by implementing "ConfigIncluder" but I have not figured out how to do this.
http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigIncluder.html exists, but I don't have a good example to point to. The general outline of how this works is that you set an includer on your ConfigParseOptions, then the library is going to call yourIncluder.withFallback(defaultIncluder)
and you can capture the default includer in the result of your withFallback
, which allows you to chain to it when you implement the include
method.
After a few tries to get what you said to work, I have figured out a solution that seems to work for what I wanted.
instead of 'include file("plugins/[plugin name]/foo.conf")' I can use 'include "foo.conf"'
public static class Includer implements ConfigIncluder {
@Override
public ConfigIncluder withFallback(ConfigIncluder fallback) {
return this;
}
@Override
public ConfigObject include(ConfigIncludeContext context, String what) {
File file = new File("plugins/[plugin name]/" + what);
return ConfigFactory.parseFile(file).root();
}
}
Thanks for your input
That looks right to me. If you wanted to chain to the regular default includer, you would add a field ConfigIncluder fallback
, then in withFallback you copy your includer but with that field set. And then implement whatever logic you want for when to use it, perhaps if the plugins file doesn't exist you chain to the default includer or something.
There are also extra interfaces for implementing file() etc if I remember right. ConfigIncluderFile etc.
I am unsure what I missed, but I tested it with and without the includer I made and 'include "foo.conf"' seems to do exactly what I want in both cases. (that is look for 'foo.conf' relative to the file the 'include' was found in)
I have to be missing something, if this is the case then I never needed to make a custom includer in the first place. It could be that I always used 'include file("foo.conf")' before and never tried it without the file().
Am I missing something?
https://github.com/typesafehub/config/blob/master/HOCON.md#include-semantics-locating-resources is how it's supposed to work (maybe it even does!)
Yes, foo.conf is supposed to be found relative to the file that included foo.conf. I apologize for not catching right away that's what you were doing.
According to the spec this does appear to be only if it's bare "foo.conf"
and not file("foo.conf")
but according to the code it looks like it's trying to make file("foo.conf")
work the same way (which does make sense to me). See https://github.com/typesafehub/config/blob/master/config/src/main/java/com/typesafe/config/impl/Parseable.java#L522 for example.
The spec is a little unclear even to me; it says the bare include "foo.conf"
would look adjacent to the including file or resource, but it just doesn't really say what file()
should do.
I guess the first step is to figure out what the current code actually does and whether we have tests for it. Then we can figure out how to clarify the spec. The spec may end up having to document whatever the code does, unless the code is doing something utterly useless that nobody could be relying on.
If I put a throw new AssertionError("HERE")
in ParseableFile.relativeTo where it gets an adjacent file, I get the following tests failing:
[error] Test com.typesafe.config.impl.PublicApiTest.includesCanBeMissingThoughFileCannot failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.includersAreUsedWithFiles failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.includersAreUsedRecursivelyWithFiles failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.fullIncluderNotUsedWithoutNewSyntax failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.includersAreUsedRecursivelyWithURL failed: HERE
[error] Test com.typesafe.config.impl.EquivalentsTest.testEquivalents failed: HERE
None of those sound like they are directly testing this feature in normal usage, though, so I wouldn't say we've proven it works.
I added PR #277 which tests and documents the current behavior. Right now only the "heuristic" includes (without file()) do the "look adjacent" trick. I think we should probably change this, though it would technically be a semantic ABI break. But the code needs to be reshuffled a bit to make this change, it isn't 100% trivial. In the meantime the situation is "works as designed, but design decision was kinda bad."
any updates on this? I'm having problems including a configuration from a file located in a parent directory, but I don't have problems when including from the same directory or children directory... any idea?
No idea offhand. I guess you are trying a thing like include "../foo.conf"
? I don't think there's a test case for that in the test suite, I don't know whether it works, I think it should work in the same cases that include "./foo.conf"
does, but perhaps it does not if nobody has ever tried it until now.
After a bunch of attempts, I'm fairly certain that include "../foo.conf
does not work. Unfortunately, due to #122, this makes it hard to add a magic default that would load an environment.conf
from certain places relative to the project or in the user's homedir. I'll probably load environment.conf
using -Dconfig.file
, then add include "application"
to that instead.
Referencing a local path is not working as follows:
For the following configuratoin:
sqls = {
scoring = {
scoringSummarySql = """select MatchMethod, F1, rsF1, Precision, Recall, TP, FP, FN from scoring"""
}
This works if the above snippet is directly inside the calling file - but gives the following error if using the following include directive:
include "matching-sqls.conf"
String: 117: Could not resolve substitution to a value: ${sqls.scoring.scoringSummarySql}
com.typesafe.config.ConfigException$UnresolvedSubstitution: String: 117: Could not resolve substitution to a value: ${sqls.scoring.scoringSummarySql}
I am fine to do any kind of relative or local path for includes - but is anything working presently?
I'm having problems including a configuration from a file located in a parent directory, but I don't have problems when including from the same directory or children directory... any idea?
I've found traversing the whole resource path easier to include a file from parent directory
Let's say you have a/b/c/d.conf
doing an include "../../e.conf"
to actually include a/e.conf
, it doesn't work. You need to do include "/a/e.conf"
This is a major bug that my project cannot live with, since we'd like all our include paths to be relative within a git repository. Here is an easy way to reproduce the bug:
- clone https://github.com/vlad2/hocon-config-printer and install
- create the following directory structure and files
cat subdir/a.conf
{ a: b }
cat subdir/b.conf
{ include file("a.conf") c: ${a} }
-
hocon-config-printer subdir/b.conf -resolve
Exception in thread "main" com.typesafe.config.ConfigException$UnresolvedSubstitution: subdir/b.conf: 3: Could not resolve substitution to a value: ${a} at com.typesafe.config.impl.ConfigReference.resolveSubstitutions(ConfigReference.java:104) at com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183) at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146) at com.typesafe.config.impl.SimpleConfigObject$ResolveModifier.modifyChildMayThrow(SimpleConfigObject.java:380) at com.typesafe.config.impl.SimpleConfigObject.modifyMayThrow(SimpleConfigObject.java:313) at com.typesafe.config.impl.SimpleConfigObject.resolveSubstitutions(SimpleConfigObject.java:399) at com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183) at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146) at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:235) at com.typesafe.config.impl.SimpleConfig.resolveWith(SimpleConfig.java:79) at com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:69) at com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:64) at com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:42) at ro.vdin.configprinter.ConfigPrinter.printConfig(ConfigPrinter.java:15) at ro.vdin.configprinter.ConfigPrinterMain.main(ConfigPrinterMain.java:24) Caused by: com.typesafe.config.impl.AbstractConfigValue$NotPossibleToResolve: was not possible to resolve at com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:180) at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146) at com.typesafe.config.impl.ConfigReference.resolveSubstitutions(ConfigReference.java:88) ... 14 more
- running the parser under subdir does work:
hocon-config-printer b.conf -resolve
{ "a" : "b", "c" : "b" }
Is there even a reasonable workaround? I think bare include "foo/bar.conf"
is not platform independent because of the "/"?