[Handlebars] Handlebars.java 버전의 with helper 버그 패치 커밋
회사에서 Handlebars.java 와 관련된 이슈가 공유가 되었다.
Handlebars 가 Javascript 버전과 Java 버전의 #with helper 결과가 동일하지 않습니다.
우선 이 이슈 버그를 해결한 코드는 필자의 github 저장소 https://github.com/powerumc/handlebars.java.bug-fix 에 커밋 되어 있고, 원본 저장소의 이슈 번호 #314, Pull Request #315 에 등록 되었다.
Handlebars vs Handlebars.java
이 테스트에서 사용되는 handlebars 데이터는 다음과 같습니다.
{ "company": { "ko": "쿠팡",
"en": "Coupang" }}
그리고 handlebars 템플릿은 다음과 같다.
{{#with company}}
우리 회사는 {{ko}}
My company is {{company.en}}
{{/with}}
- Javascript handlebars 결과
우리 회사는 쿠팡
My company is
- Handlebars.java 결과
우리 회사는 쿠팡
My company is Coupang
문제 원인
Handlebars.java 를 반나절 정도 분석하고 나니 대충 (정말 대충) 어떻게 흘러가는 지 조금 이해가 되었다.
문제는 Handlebars.java 에서는 #with helper 에서 value 값을 resolving 하지 못하면 parent object (=context) 에서 찾는다. #with helper 에게 전달된 데이터를 model 이라고 하면 전달된 데이터 전체를 context 로 불린다. 그래서 model 의 parent 는 context 가 된다.
위의 상황을 보면 My company is {{company.en}}
와 같은 코드의 model 에서 {#with company}}
의 company.en
을 찾지 못해서 context 에서 company.en
을 찾게 된다.
문제 해결 방법
문제의 원인을 파악했으니 코드를 디버깅해 알겠지만 튜닝 포인트가 매우 다양하다. #with helper 전체를 뜯어 고칠 수 도 있고, 내부적으로 CompositeValueResolver 를 튜닝하거나, 그 외에 다양한 방법으로 고칠 수 있다.
가장 간단하게 이 버그를 픽스하기 위해 또 반나절 정도를 적용해 보고, 가장 적은 코드로 튜닝할 수 있는 코드를 만들었다.
Options.java 코드에서 wrap(final Object)
메서드를 다음과 같이 픽스하였다. 딱 세 줄만 추가해 주면 된다.
public Context wrap(final Object model) {
if (model == context) {
return context;
}
if (model == context.model()) {
return context;
}
if (model instanceof Context) {
return (Context) model;
}
if (model.getClass() == LinkedHashMap.class) { // 이 코드부터...
return Context.newContext(model);
} // 여기까지 추가...
return Context.newContext(context, model);
}
그럼 아래의 이슈가 되는 테스트 코드가 무사히 통과하고,Handlebars.java 의 모든 테스트도 통과한다.
public class Issue314 extends AbstractTest {
@Test
public void withHelperSpec() throws IOException {
String context = "{ obj: { context: { one: 1, two: 2 } } }";
shouldCompileTo("{{#with obj}}{{context.one}}{{/with}}", context, "1");
shouldCompileTo("{{#with obj}}{{obj.context.one}}{{/with}}", context, "");
}
}