-
Notifications
You must be signed in to change notification settings - Fork 47
Allow deep data in TemplateProcessor #30
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
base: next
Are you sure you want to change the base?
Conversation
|
Possibly related to #21 |
|
This is really good stuff, but I will need to ask your patience while I review. Thank you so much for your help! |
|
I've been reviewing, this is looking good. A few design points:
|
|
This makes the processor more lenient but still strict enough to not miss any key.
|
I reworked the processor to accept any type of data, as long as it's an Object. @byronka can you do a review again? |
|
I appreciate your ambition with these changes, but I think this requires a bit more design, or possibly pulling back somewhat. This is a large change, and it is hard for me to comprehend all the consequences. I am concerned to to make such significant changes without a pressing need. Some notes: The documentation above the new The design to use "value" as a special keyword for templates is contrary to the premise of keeping simple. A user would need to remember that a key of "value" has special significance. Using Even before this change, it was questionable that the data could be provided in different ways, like pre-registering ( All templates will end up as a string. Thus, inputs can reliably always be strings, so any necessary processing can be done beforehand. If something goes awry, the user is able to put a breakpoint on an accessible spot in their own code to see exactly what went into the template. There's essentially no adjustments made after the data goes in. Typical templating tools enable all sorts of sigil-and-convention-driven auto-magic mischief on data, which I would prefer to avoid. These changes did raise the issue of the complexity of handling twice-nested templates. Right now there's some edge cases. On master, you can see how in The performance is slightly worsened in this change. Taking averages, I calculated this as about 7% slower with checks and 11% slower without checks. For the following, I ran the test |
|
If we were going to reduce moving parts in TemplateProcessor, I would suggest removing the use of INNER_TEMPLATE and the alternative way of |
|
The problem with the current code is that when we can use the same processor for different list of map data, we can't do that in inner template. Inner template is treated the same as Static Data, with a fancy hat of "indentation". That means we cannot use the same inner template for different data. If we want to do that, we have to construct another processor for that exact data, which defeats the purpose of using one processor for different threads of different data. |
|
I think we can remove the use of inner template and just support multi-line string directly. |
|
I propose a simpler processor in #32 |
I'm not sure I understand this - could you provide a simple test to demonstrate what you mean? |
In short, this test would fail @Test
public void test_Template_Multi_Thread_WithInner() {
TemplateProcessor templateProcessor = buildProcessor("Hello {{thread}}");
TemplateProcessor innerTemplate = buildProcessor("Thread #{{name}}");
templateProcessor.registerInnerTemplate("thread", innerTemplate);
int threadCount = 10;
var futures = new ArrayList<CompletableFuture<String>>();
// Create multiple concurrent render tasks with different data
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
var future = CompletableFuture.supplyAsync(() -> {
templateProcessor.getInnerTemplate("thread").registerData(List.of(Map.of("name", Integer.toString(threadNum))));
return templateProcessor.renderTemplate();
});
futures.add(future);
}
// Verify all threads completed successfully with correct results
for (int i = 0; i < threadCount; i++) {
String result = futures.get(i).join();
assertEquals(result, "Hello Thread #" + i);
}
}The test is a modification of The point is that we can only do that if we want to change the data in the inner template, but since the But that brings us to another question: Since the inner template always use its field value Changing the test to be like this would make it passed @Test
public void test_Template_Multi_Thread_WithInner() {
TemplateProcessor templateProcessor = buildProcessor("Hello {{thread}}");
TemplateProcessor innerTemplate = buildProcessor("Thread #{{name}}");
int threadCount = 10;
var futures = new ArrayList<CompletableFuture<String>>();
// Create multiple concurrent render tasks with different data
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
var future = CompletableFuture.supplyAsync(() -> {
String thread = innerTemplate.renderTemplate(Map.of("name", Integer.toString(threadNum)));
return templateProcessor.renderTemplate(Map.of("thread", thread));
});
futures.add(future);
}
// Verify all threads completed successfully with correct results
for (int i = 0; i < threadCount; i++) {
String result = futures.get(i).join();
assertEquals(result, "Hello Thread #" + i);
}
}But then at this point I don't see the reason to use inner template. The possible case I can think of is to create a common template and assign it as inner template to multiple templates. Like making a HTML TemplateProcessor headerTemplate = buildProcessor("<title>{{title}}</title>");
TemplateProcessor homeTemplate = buildProcessor("...");
homeTemplate.registerInnerTemplate("header", headerTemplate);
homeTemplate.getInnerTemplate("header").registerData(List.of(Map.of("title", "Homepage")));
TemplateProcessor adminTemplate = buildProcessor("...");
adminTemplate.registerInnerTemplate("header", headerTemplate);
adminTemplate.getInnerTemplate("header").registerData(List.of(Map.of("title", "Admin Page")));But it's a bit overkill, and the processor would waste time rendering the same The simpler way is just doing a string replacement before constructing the page processors: TemplateProcessor headerTemplate = buildProcessor("<title>{{title}}</title>");
TemplateProcessor homeTemplate = buildProcessor("...".replace("{{title}}", headerTemplate.renderTemplate(Map.of("title", "Homepage"))));
TemplateProcessor adminTemplate = buildProcessor("...".replace("{{title}}", headerTemplate.renderTemplate(Map.of("title", "Admin Page"))));This way we only call Based on that, I suggest we consider the possibility of removing the use of Inner Template. I made an example in #32. |
As I mentioned in the previous PR, this one is to allow us to set a complex data map with keys from main and inner template.
An example for it can be seen in the tests: