-
Notifications
You must be signed in to change notification settings - Fork 18
Home
When evaluating simple expressions, opel use only OpelEngine
class and eval
method. Remember that opel
always return CompletableFuture.
String expression = "2 * 3 + 4";
OpelEngine engine = OpelEngineBuilder.create().build()
engine.eval(expression)
.whenComplete((result, error) -> System.out.println(result));
this expression is transformed to equivalent code:
CompletableFuture.completedFuture(2)
.thenCombine(CompletableFuture.completedFuture(3), (l, r) -> l * r)
.thenCombine(CompletableFuture.completedFuture(4), (l, r) -> l + r)
.whenComplete((result, error) -> System.out.println(result));
As you see, opel
efficiently hides boilerplate of the CompletableFuture API.
Simle expressions like 2+2*2
are executed in the same thread in which the method eval
was called.
To taste the real power of opel
asynchrony, you have to add a source of time-consuming operations.
To create custom function, OpelAsyncFunction<T>
interface has to be implemented. It is an implementation of the well known adapter design pattern.
public class ExampleTemperatureFunction implements OpelAsyncFunction<BigDecimal> {
@Override
public CompletableFuture<BigDecimal> apply(List<CompletableFuture<?>> args) {
return args.get(0).thenApply(city -> {
// add code to call external service about temperature in given city
return BigDecimal.valueOf(22);
});
}
}
When you build OpelEngine
by OpelEngineBuilder
you can simply add functions to it (how to implement own function is present in Building custom function section):
OpelEngine engine = OpelEngineBuilder.create()
.withFunction("myFunctionName", new ExampleTemperatureFunction())
.build()
Then myFunctionName is avalable in engine and can be call:
String expression = "myFunctionName('arg1', 'arg2') * 100";
engine.eval(expression)
.whenComplete((result, error) -> System.out.println(result));
In the same way as the functions you can register values using OpelEngineBuilder
. You can add the completed values as well as CompletableFuture
:
OpelEngine engine = OpelEngineBuilder.create()
.withVariable("a", CompletableFuture.completedValue(2))
.withCompletedVariable("b", 5)
.build();
String expression = "a*a + b";
engine.eval(expression)
.whenComplete((result, error) -> System.out.println(result));
In many sytuation functions results or values depend on some context. For example in wab application custom function returns information about signed in user depends on HTTP request. In opel
the solution for that issue is EvalContext
interface. You can provide your functions and values implementing your own EvalContext
or you can build it using EvalContextBuilder
- it is similar to register function and values in engine:
OpelEngine engine = OpelENgineBuilder.build();
EvalContext context = EvalContextBuilder.create()
.withCompletedVariable('userName', 'Signed in uest name')
.withFunction('userInfo', new UserInfoFunction()) // do not forget to create custom function UserInfoFunction
.build();
String expression = "'Hello' + userName";
engine.eval(expression, context)
.whenComplete((result, error) -> System.out.println(result));
If function or value is provided by both EvalContext
and is registered in Engine
, value from context override engine value - context has higher priority:
OpelEngine engine = OpelENgineBuilder
.withCompletedVariable('userName', 'anonymous')
.build();
EvalContext context = EvalContextBuilder.create()
.withCompletedVariable('userName', 'Elvis Presley') //this value override value from engine
.build();
String expression = "'Hello' + userName";
engine.eval(expression, context)
.whenComplete((result, error) -> System.out.println(result)); // print 'Hello Elvis Presley'