Change scope constructor from private to protected
fjtirado opened this issue · 3 comments
It will be great if scope class can be extended (although the class is not final, the construtor is private)
For example, to dynamically retrieve variable values from an extenal source by overriding getValue method.
AFAIK, the only way to change the behaviour of getValue is by invoking setValue, but there might be cases where your variable source (for example a DB) has a dynamic nature.
You are right, currently, variable values must be pre-computed and there's no way to do dynamic or lazy evaluation. The closest you can do is to implement a custom function instead of using a variable:
scope.addFunction("foo", 0, new Function() {
@Override
public void apply(Scope scope, List<Expression> args, JsonNode in, Path path, PathOutput output, Version version) throws JsonQueryException {
JsonNode value = ... // obtain the value here
output.emit(value, null);
}
});
I'm willing to support dynamic/lazy variables, but rather than allowing subclassing the scope class, I want to add a new setValue overload that takes a ValueSource instance:
interface ValueSource {
JsonNode getValue();
}
public class Scope {
public void setValue(String name, JsonNode value) { ... };
public void setValue(String name, ValueSource valueSource) { ... }; // the new overload
}
// This can be used like this:
// scope.setValue("foo", () -> new TextNode("this value is lazily evaluated"));
What do you think? Does this work for you?
@eiiches Thanks for the response.
Yes, this new setValue method will do the trick.
I use a similar idea to implement a hack some time ago to support some prefixed JQ variables here, but now I have the luxury of waiting for you to add this new functionality ;)
Once you add the new function method, I can replace the FunctionJsonNode hack by just a lambda.
@eiiches Should I open a PR implementing what we discussed? I think the method to be added should be
public interface ValueSource extends Function<String,JsonNode> {
}
public void setValue(ValueSource valueSource) { ... }; // the new overload
So you can write
scope.setValue( k -> new TextNode(k+" value is lazily evaluated"));
The point is that you do not know if your external source can resolve a particular key at the moment you are configuring the scope