Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return empty string for un-evaluated lazy expression. #405

Merged
merged 2 commits into from
Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/main/java/com/hubspot/jinjava/interpret/LazyExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import com.fasterxml.jackson.annotation.JsonValue;

public class LazyExpression implements Supplier {

private final Supplier supplier;
private final String image;
private Object jsonValue = null;

private LazyExpression(Supplier supplier, String image) {
this.supplier = supplier;
Expand All @@ -19,13 +19,20 @@ public static LazyExpression of(Supplier supplier, String image) {
}

@Override
@JsonValue
public Object get() {
return supplier.get();
if (jsonValue == null) {
jsonValue = supplier.get();
}
return jsonValue;
}

@Override
public String toString() {
return image;
}

@JsonValue
public Object getJsonValue() {
return jsonValue == null ? "" : jsonValue;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it evaluate the supplier but catch any exception and return empty string?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good question. It depends what the serialization wants. What I wanted here is to get the current state -- no more evaluation. Maybe there is a need to finalize any un-evaluated expression.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LazyExpression was created as a more under-the-hood optimization where we could use it to prevent computing something if it wasn't needed. It would be still useful to compute when debugging information.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When debugging, you either just use the toString(), and/or with get() to evaluate it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am talking from the developer perspective. Developers depend on the developer info button that displays the context.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it work now? It will throw exceptions for serialization. If it is not using the json serialization, it does not affect anything.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the serialization breaks if the get() method throws an exception (in the case of the NPE). We should also at least fall back on the image property.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume when we store the lazyExpression in somewhere, we assume to store the value, only when we need it. However, the serialization should not execute the function because it may have side effect -- which may cause concurrent modification exception. I thought the image is the expression itself. I think they should be evaluate to empty, but I am OK if you change to keep it.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,31 @@

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import org.junit.Test;

public class LazyExpressionTest {

@Test
public void itSerializesUnderlyingValue() throws JsonProcessingException {
LazyExpression expression = LazyExpression.of(() -> ImmutableMap.of("test", "hello", "test2", "hello2"), "{}");
assertThat(new ObjectMapper().writeValueAsString(expression)).isEqualTo("{\"test\":\"hello\",\"test2\":\"hello2\"}");
LazyExpression expression = LazyExpression.of(
() -> ImmutableMap.of("test", "hello", "test2", "hello2"),
"{}"
);
Object evaluated = expression.get();
assertThat(evaluated).isNotNull();
assertThat(new ObjectMapper().writeValueAsString(expression))
.isEqualTo("{\"test\":\"hello\",\"test2\":\"hello2\"}");
}

@Test
public void itSerializesNonEvaluatedValueToEmpty() throws JsonProcessingException {
LazyExpression expression = LazyExpression.of(
() -> ImmutableMap.of("test", "hello", "test2", "hello2"),
"{}"
);
assertThat(new ObjectMapper().writeValueAsString(expression)).isEqualTo("\"\"");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class UnixTimestampFunctionTest {

@Test
public void itGetsUnixTimestamps() {
assertThat(Functions.unixtimestamp()).isGreaterThan(0).isLessThan(System.currentTimeMillis());
assertThat(Functions.unixtimestamp()).isGreaterThan(0).isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(Functions.unixtimestamp(epochMilliseconds)).isEqualTo(epochMilliseconds);
assertThat(Functions.unixtimestamp(d)).isEqualTo(epochMilliseconds);
assertThat(Math.abs(Functions.unixtimestamp(null) - ZonedDateTime.now().toEpochSecond() * 1000)).isLessThan(1000);
Expand Down