handlebars์ ๋ํ ๊ณต๋ถ๋ฅผ ํ๋ ๋์ค, jknack/handlebars.java ํ๋ก์ ํธ์ README.md์ handlebars.java์ ๋ํ ์ค๋ช ์ด ๋งค์ฐ ์ ๋์์์ด ์ด๋ฅผ ํ๊ตญ์ด๋ก ๋ฒ์ญํ์ต๋๋ค.
Handlebars.java
Logic-less and semantic Mustache templates with Java
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hello {{this}}!");
System.out.println(template.apply("Handlebars.java"));
Output:
Hello Handlebars.java!
Handlebars.java๋ Java๋ฅผ ์ํ handlebars ์ ๋๋ค.
Handlebars๋ ํจ๊ณผ์ ์ผ๋ก ์๋งจํฑ ํ ํ๋ฆฟ์ ๊ตฌ์ถํ๋๋ฐ ํ์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
Mustache ํ ํ๋ฆฟ์ ํธ๋ค๋ฐ์ ํธํ๋๋ฏ๋ก Mustache ํ ํ๋ฆฟ์ ๊ฐ์ ธ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ถ๊ฐ ํธ๋ค๋ฐ ๊ธฐ๋ฅ์ ํ์ฉํ์ญ์์ค.
Requirements
- Handlebars 4.3+๋ Java 8 ์ด์์ ๋ฒ์ ์ ์๊ตฌํฉ๋๋ค.
Getting Started
์ผ๋ฐ์ ์ผ๋ก Handlebars ํ ํ๋ฆฟ์ ๊ตฌ๋ฌธ์ Mustache ํ ํ๋ฆฟ์ ์์ ์งํฉ์ ๋๋ค. ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์ Mustache manpage๋ฅผ ํ์ธํ์ธ์.
Handlebars.java blog๋ ์์ํ๊ธฐ์ ์ข์ ๊ณณ์ ๋๋ค. Javadoc์ javadoc.io์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Maven
Stable version: 
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<version>${handlebars-version}</version>
</dependency>
templates ๋ก๋ฉ
ํ
ํ๋ฆฟ์ TemplateLoader
ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋๋ฉ๋๋ค. Handlebars.java๋ TemplateLoader
์ ์ธ ๊ฐ์ง ๊ตฌํ์ ์ ๊ณตํฉ๋๋ค.
- ClassPathTemplateLoader (default)
- FileTemplateLoader
- SpringTemplateLoader (handlebars-springmvc module ์ฐธ๊ณ )
์ด ์์ ๋ ํด๋์ค ๊ฒฝ๋ก์ ๋ฃจํธ์์ mytemplate.hbs
๋ฅผ ๋ก๋ํฉ๋๋ค.
mytemplate.hbs:
Hello {{this}}!
Handlebars handlebars = new Handlebars();
Template template = handlebars.compile("mytemplate");
System.out.println(template.apply("Handlebars.java"));
Output:
Hello Handlebars.java!
๋ค์๊ณผ ๊ฐ์ด ๋ค๋ฅธ TemplateLoader
๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
TemplateLoader loader = ...;
Handlebars handlebars = new Handlebars(loader);
Templates ์ ๋์ฌ ๋ฐ ์ ๋ฏธ์ฌ
'TemplateLoader'๋ ๋ ๊ฐ์ง ์ค์ํ ์์ฑ์ ์ ๊ณตํฉ๋๋ค.
prefix
: ํ ํ๋ฆฟ์ด ์ ์ฅ๋๋ ๊ธฐ๋ณธ ์ ๋์ฌ๋ฅผ ์ค์ ํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค.suffix
: ํ ํ๋ฆฟ์ ๊ธฐ๋ณธ ์ ๋ฏธ์ฌ ๋๋ ํ์ผ ํ์ฅ์๋ฅผ ์ค์ ํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์.hbs
์ ๋๋ค.
Example:
TemplateLoader loader = new ClassPathTemplateLoader();
loader.setPrefix("/templates");
loader.setSuffix(".html");
Handlebars handlebars = new Handlebars(loader);
Template template = handlebars.compile("mytemplate");
System.out.println(template.apply("Handlebars.java"));
Handlebars.java๋ mytemplate
์ /templates/mytemplate.html
๋ก ํด์ํ๊ณ ๋ก๋ํฉ๋๋ค.
The Handlebars.java ์๋ฒ
handlebars.java ์๋ฒ๋ Mustache/Handlebars ํ ํ๋ฆฟ์ ์์ฑํ๊ณ ๋ฐ์ดํฐ์ ๋ณํฉํ ์ ์๋ ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค.
์น ๋์์ด๋์๊ฒ ์ ์ฉํ ๋๊ตฌ์ ๋๋ค.
Maven Central์์ ๋ค์ด๋ก๋:
- here ํด๋ฆญ
- Download ์น์ ์์ jar๋ฅผ ํด๋ฆญํฉ๋๋ค.
Maven:
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-proto</artifactId>
<version>${current-version}</version>
</dependency>
Usage:java -jar handlebars-proto-${current-version}.jar -dir myTemplates
Example:
myTemplates/home.hbs
<ul>
{{#items}}
{{name}}
{{/items}}
</ul>
myTemplates/home.json
{
"items": [
{
"name": "Handlebars.java rocks!"
}
]
}
๋๋ YAML myTemplates/home.yml์ ์ ํธํ๋ ๊ฒฝ์ฐ:
items:
- name: Handlebars.java rocks!
๋ค์ ์ฃผ์์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฝ๋๋ค.
http://localhost:6780/home.hbs
์ถ๊ฐ ์ต์ :
- -dir: ํ ํ๋ฆฟ ๋๋ ํ ๋ฆฌ ์ค์
- -prefix: ํ ํ๋ฆฟ์ ์ ๋์ฌ๋ฅผ ์ค์ ํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ /์ ๋๋ค.
- -suffix: ํ ํ๋ฆฟ์ ์ ๋ฏธ์ฌ๋ฅผ ์ค์ ํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ .hbs์ ๋๋ค.
- -context: ์ปจํ ์คํธ์ ๊ฒฝ๋ก๋ฅผ ์ค์ ํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ /์ ๋๋ค.
- -port: ํฌํธ ๋ฒํธ ์ค์ , ๊ธฐ๋ณธ๊ฐ์ 6780
--content-type: ์ปจํ ์ธ ์ ํ ํค๋๋ฅผ ์ค์ ํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ text/html์ ๋๋ค.
template ๋น ๋ค์์ data sources
๋จ์ผ ํ ํ๋ฆฟ์ ๋ํด ๋ค์์ ๋ฐ์ดํฐ์ ์ ํ ์คํธํด์ผ ํ๊ฑฐ๋ ํ ์คํธํ๋ ค๋ ๊ฒฝ์ฐ ์์ฒญ URI์์ 'data' ๋งค๊ฐ๋ณ์๋ฅผ ์ค์ ํ์ฌ ์ํํ ์ ์์ต๋๋ค.
Example:
http://localhost:6780/home.hbs?data=mytestdata
ํ์ฅ ํ์ผ์ ์ง์ ํ ํ์๊ฐ ์์ต๋๋ค.
Helpers
Built-in helpers:
- with
- each
- if
- unless
- log
- block
- partial
- precompile
- embedded
- i18n and i18nJs
- string helpers
- conditional helpers
with, each, if, unless:
built-in helper documentation๋ฅผ ์ฐธ์กฐํ์ธ์.
block and partial
Block and partial helpers๊ฐ ํจ๊ป ์๋ํ์ฌ Template Inheritance์ ์ ๊ณตํฉ๋๋ค.
Usage:
{{#block "title"}}
...
{{/block}}
context: region์ ์ด๋ฆ์ ์ ์ํ๋ ๋ฌธ์์ด ๋ฆฌํฐ๋ด.
Usage:
{{#partial "title"}}
...
{{/partial}}
context: region์ ์ด๋ฆ์ ์ ์ํ๋ ๋ฌธ์์ด ๋ฆฌํฐ๋ด.
precompile
handlebars.js๋ฅผ ์ฌ์ฉํ์ฌ Handlebars.java ํ ํ๋ฆฟ์ JavaScript๋ก Precompile
user.hbs
Hello {{this}}!
home.hbs
<script type="text/javascript">
{{precompile "user"}}
</script>
Output:
<script type="text/javascript">
(function () {
var template = Handlebars.template,
templates = (Handlebars.templates = Handlebars.templates || {});
templates["user"] = template(function (
Handlebars,
depth0,
helpers,
partials,
data
) {
helpers = helpers || Handlebars.helpers;
var buffer = "",
functionType = "function",
escapeExpression = this.escapeExpression;
buffer += "Hi ";
depth0 = typeof depth0 === functionType ? depth0() : depth0;
buffer += escapeExpression(depth0) + "!";
return buffer;
});
})();
</script>
๋ค์์ ์ฌ์ฉํ์ฌ ๋ฏธ๋ฆฌ ์ปดํ์ผ๋ ํ ํ๋ฆฟ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
var template = Handlebars.templates["user"];
๊ธฐ๋ณธ์ ์ผ๋ก /handlebars-v1.3.0.js
๋ฅผ ์ฌ์ฉํ์ฌ ํ
ํ๋ฆฟ์ ์ปดํ์ผํฉ๋๋ค. handlebars.java 2.x๋ถํฐ handlebars.js 2.x๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
Handlebars handlebars = new Handlebars();
handlebars.handlebarsJsFile("/handlebars-v2.0.0.js");
๋ ์์ธํ ์ ๋ณด๋ Precompiling Templates ๋ฌธ์์์ ํ์ธํ ์ ์์ต๋๋ค.
Usage:
{{precompile "template" [wrapper="anonymous, amd or none"]}}
context: ํ์๋ก ๋ค์ด๊ฐ์ผํ๋ template์ ์ด๋ฆ์ ๋๋ค.
wrapper: "anonymous", "amd" or "none" ์ค ํ๋์ ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ "anonymous"
maven plugin๋ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
embedded
The embedded helper๋ฅผ ์ฌ์ฉํ๋ฉด <script>
HTML ํ๊ทธ ์์ ํธ๋ค๋ฐ ํ
ํ๋ฆฟ์ "ํฌํจ"ํ ์ ์์ต๋๋ค.
user.hbs
<tr>
<td>{{firstName}}</td>
<td>{{lastName}}</td>
</tr>
home.hbs
<html>
... {{embedded "user"}} ...
</html>
Output:
<html>
...
<script id="user-hbs" type="text/x-handlebars">
<tr>
<td>{{firstName}}</td>
<td>{{lastName}}</td>
</tr>
</script>
...
</html>
Usage:
{{embedded "template"}}
context: ํ์๋ก ๋ค์ด๊ฐ์ผํ๋ template์ ์ด๋ฆ์ ๋๋ค.
i18n
{@link ResourceBundle} ์์ ๊ตฌ์ถ๋ helper์ ๋๋ค. {@link ResourceBundle}์ Java์์ ๊ฐ์ฅ ์ ์๋ ค์ง ๊ตญ์ ํ(i18n) ๋ฉ์ปค๋์ฆ์ ๋๋ค.
Usage:
{{i18n "hello"}}
์ด๋ฅผ ์ํด์๋ classpath์ ๋ฃจํธ์ messages.properties
๊ฐ ํ์ํฉ๋๋ค.
Using a locale:
{{i18n "hello" locale="es_AR"}}
์ด๋ฅผ ์ํด์๋ classpath์ ๋ฃจํธ์ messages_es_AR.properties
๊ฐ ํ์ํฉ๋๋ค.
๋ค๋ฅธ bundle ์ฌ์ฉ:
{{i18n "hello" bundle="myMessages"}}
์ด๋ฅผ ์ํด์๋ ํด๋์ค ๊ฒฝ๋ก์ ๋ฃจํธ์ 'myMessages.properties'๊ฐ ํ์ํฉ๋๋ค.
message format ์ฌ์ฉ:
{{i18n "hello" "Handlebars.java"}}
hello
๊ฐ Hola {0}!
์ธ ๊ฒฝ์ฐ ๊ฒฐ๊ณผ๋ Hola Handlebars.java!
์
๋๋ค.
i18nJs
'ResourceBundle'์ JavaScript ์ฝ๋๋ก ๋ฒ์ญํฉ๋๋ค. ์์ฑ๋ ์ฝ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ I18n์ด ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
Usage:
{{i18nJs [locale] [bundle=messages]}}
locale ์ธ์๊ฐ ์์ผ๋ฉด ํด๋น ๋ก์ผ์ผ์ JavaScript๋ก ๋ณํํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ locale์ ์ฌ์ฉํฉ๋๋ค.
์์ฑ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
<script type="text/javascript">
I18n.defaultLocale = 'es_AR';
I18n.locale = 'es_AR';
I18n.translations = I18n.translations || {};
// Spanish (Argentina)
I18n.translations['es_AR'] = {
"hello": "Hi {{arg0}}!"
}
</script>
๋ง์ง๋ง์ผ๋ก Hi {0}
์ ๊ฐ์ ๋ฉ์์ง ํจํด์ Hi {{arg0}}
๋ก ๋ณํํฉ๋๋ค. ์ด๊ฒ์ I18n JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ณ์๋ฅผ ๋ณด๊ฐํ๋ ๊ฒ์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
string helpers
StringHelpers์์ ์ฝ์ด, ๋๋ฌธ์, ์กฐ์ธ, dateFormat, yesno ๋ฑ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. github/jknack/handlebars/helper/StringHelpers.java).
NOTE: string helpers๋ฅผ ๋ฑ๋กํด์ผ ํฉ๋๋ค(๊ธฐ๋ณธ์ ์ผ๋ก ์ถ๊ฐ๋์ง ์์).
conditional helpers
eq, neq, lt, gt, and, or, not ๋ฑ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ [ConditionalHelpers](https://github.com/jknack/handlebars.java/blob/master/handlebars/src/main/ java/com/github/jknack/handlebars/helper/ConditionalHelpers.java)์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
NOTE: conditional helpers๋ฅผ ๋ฑ๋กํด์ผ ํฉ๋๋ค(๊ธฐ๋ณธ์ ์ผ๋ก ์ถ๊ฐ๋์ง ์์).
TypeSafe Templates
TypeSafe Templates๋ TypeSafeTemplate
interface๋ฅผ ํ์ฅํ์ฌ ์์ฑ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด:
// 1
public static interface UserTemplate extends TypeSafeTemplate<User> {
// 2
public UserTemplate setAge(int age);
public UserTemplate setRole(String role);
}
// 3
UserTemplate userTmpl = handlebars.compileInline("{{name}} is {{age}} years old!")
.as(UserTemplate.class);
userTmpl.setAge(32);
assertEquals("Edgar is 32 years old!", userTmpl.apply(new User("Edgar")));
TypeSafeTemplate
interface๋ฅผ ํ์ฅํฉ๋๋ค.- ํ์ํ ๋ชจ๋ ์ค์ ๋ฐฉ๋ฒ์ ์ถ๊ฐํฉ๋๋ค. set ๋ฉ์๋๋ 'void' ๋๋ 'TypeSafeTemplate' ๊ฐ์ฒด๋ฅผ ๋ฐํํ ์ ์์ต๋๋ค.
as()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์๋ก์ด ์ ํ ์์ ํ ํ๋ฆฟ์ ๋ง๋ญ๋๋ค.
Registering Helpers
Registering Helpers๋ฅผ ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
Helper
interface๋ฅผ ์ฌ์ฉ
handlebars.registerHelper("blog", new Helper<Blog>() {
public CharSequence apply(Blog blog, Options options) {
return options.fn(blog);
}
});
handlebars.registerHelper("blog-list", new Helper<List<Blog>>() {
public CharSequence apply(List<Blog> list, Options options) {
String ret = "<ul>";
for (Blog blog: list) {
ret += "<li>" + options.fn(blog) + "</li>";
}
return new Handlebars.SafeString(ret + "</ul>");
}
});
HelperSource
๋ฅผ ์ฌ์ฉ
helper source๋ "CharSequence"์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ ๊ณต์ฉ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ํด๋์ค์ ๋๋ค.
public static? CharSequence methodName(context?, parameter*, options?) {
}
์์น:
- ๋ฉ์๋๋ ์ ์ ์ผ ์๋ ์๋ ์๋ ์์ต๋๋ค.
- ๋ฉ์๋์ ์ด๋ฆ์ด helper์ ์ด๋ฆ์ด ๋ฉ๋๋ค.
- ์ปจํ ์คํธ, ๋งค๊ฐ ๋ณ์ ๋ฐ ์ต์ ์ ๋ชจ๋ ์ ํ ์ฌํญ์ ๋๋ค.
- ์ปจํ ์คํธ ๋ฐ ์ต์ ์ด ์๋ ๊ฒฝ์ฐ ๋ฉ์๋์ ์ฒซ ๋ฒ์งธ ๋ฐ ๋ง์ง๋ง ์ธ์์ฌ์ผ ํฉ๋๋ค.
๋ค์์ ๋ชจ๋ helper ๋ฉ์๋์ ์ ํจํ ์ ์์ ๋๋ค.
public class HelperSource {
public String blog(Blog blog, Options options) {
return options.fn(blog);
}
public static String now() {
return new Date().toString();
}
public String render(Blog context, String param0, int param1, boolean param2, Options options) {
return ...
}
}
...
handlebars.registerHelpers(new HelperSource());
๋๋ ์ ์ ๋ฉ์๋๋ง ์ ํธํ๋ ๊ฒฝ์ฐ:
handlebars.registerHelpers(HelperSource.class);
With plain JavaScript
1.1.0๋ถํฐ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ก helper๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
helpers.js:
Handlebars.registerHelper("hello", function (context) {
return "Hello " + context;
});
handlebars.registerHelpers(new File("helpers.js"));
๋ฉ์ง์ง ์์์?
Helper ์ต์
๋งค๊ฐ ๋ณ์
handlebars.registerHelper("blog-list", new Helper<Blog>() {
public CharSequence apply(List<Blog> list, Options options) {
String p0 = options.param(0);
assertEquals("param0", p0);
Integer p1 = options.param(1);
assertEquals(123, p1);
...
}
});
Bean bean = new Bean();
bean.setParam1(123);
Template template = handlebars.compileInline("{{#blog-list blogs \"param0\" param1}}{{/blog-list}}");
template.apply(bean);
๊ธฐ๋ณธ ๋งค๊ฐ ๋ณ์
handlebars.registerHelper("blog-list", new Helper<Blog>() {
public CharSequence apply(List<Blog> list, Options options) {
String p0 = options.param(0, "param0");
assertEquals("param0", p0);
Integer p1 = options.param(1, 123);
assertEquals(123, p1);
...
}
});
Template template = handlebars.compileInline("{{#blog-list blogs}}{{/blog-list}}");
Hash
handlebars.registerHelper("blog-list", new Helper<Blog>() {
public CharSequence apply(List<Blog> list, Options options) {
String class = options.hash("class");
assertEquals("blog-css", class);
...
}
});
handlebars.compileInline("{{#blog-list blogs class=\"blog-css\"}}{{/blog-list}}");
๊ธฐ๋ณธ hash
handlebars.registerHelper("blog-list", new Helper<Blog>() {
public CharSequence apply(List<Blog> list, Options options) {
String class = options.hash("class", "blog-css");
assertEquals("blog-css", class);
...
}
});
handlebars.compileInline("{{#blog-list blogs}}{{/blog-list}}");
Error ๋ณด๊ณ
๊ตฌ๋ฌธ errors
file:line:column: message
evidence
^
[at file:line:column]
Examples:
template.hbs
{{value
/templates.hbs:1:8: found 'eof', expected: 'id', 'parameter', 'hash' or '}'
{{value
^
partial์ ์ฐพ์ ์ ์๊ฑฐ๋ ์ค๋ฅ๊ฐ ์๋ ๊ฒฝ์ฐ ํธ์ถ ์คํ์ด ์ถ๊ฐ๋ฉ๋๋ค:
/deep1.hbs:1:5: The partial '/deep2.hbs' could not be found
{{> deep2
^
at /deep1.hbs:1:10
at /deep.hbs:1:10
Helper/Runtime errors
Helper๋ runtime errors ๋ rnans errors์ ๋๊ฐ์ง๋ฅผ ์ ์ธํ๊ณ ๋น์ทํฉ๋๋ค:
- ๋ฌธ์ ์ ์์น๊ฐ ์ ํํ ์๋ ์๊ณ ์๋ ์๋ ์์ต๋๋ค.
- stack-trace๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Examples:
Block helper:
public CharSequence apply(final Object context, final Options options) throws IOException {
if (context == null) {
throw new IllegalArgumentException(
"found 'null', expected 'string'");
}
if (!(context instanceof String)) {
throw new IllegalArgumentException(
"found '" + context + "', expected 'string'");
}
...
}
base.hbs
{{#block}} {{/block}}
Handlebars.java reports:
/base.hbs:2:4: found 'null', expected 'string'
{{#block}} ... {{/block}}
๊ฐ๋จํ ๋งํด์ helper์์ ์์ธ๋ฅผ ๋์ง ์ ์๊ณ Handlebars.java๋ ํ์ผ ์ด๋ฆ, ์ค, ์ด ๋ฐ ์ฆ๊ฑฐ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
Advanced Usage
Context stack ํ์ฅ
๋ชจ๋ ๋จ์ผ ๋ณด๊ธฐ/ํ์ด์ง์์ ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ์ก์ธ์คํด์ผ ํ๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
Context-stack์ ์ฐ๊ฒฐํ์ฌ ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ฅผ ๊ฒ์ํ ์ ์์ต๋๋ค. ์์ค ์ฝ๋๋ฅผ ํ์ธํด๋ณด์ธ์:
hookContextStack(Object model, Template template) {
User user = ....;// ๋ก๊ทธ์ธํ ์ฌ์ฉ์ ๊ฐ์ ธ์ค๊ธฐ
Map moreData = ...;
Context context = Context
.newBuilder(model)
.combine("user", user)
.combine(moreData)
.build();
template.apply(context);
context.destroy();
}
'hookContextStack' ๋ฉ์๋๋ ์ด๋์ ์์๊น์? ๊ทธ๊ฒ์ ๋น์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ํคํ ์ฒ์ ๋ฌ๋ ค ์์ต๋๋ค.
Using the ValueResolver
๊ธฐ๋ณธ์ ์ผ๋ก Handlebars.java๋ JavaBean ๋ฉ์๋(์ฆ, public getXxx ๋ฐ isXxx ๋ฉ์๋)์ Map๋ก ๊ฐ์ ํด์ํฉ๋๋ค.
๊ฐ์ ํด์ํ๋ ๊ฒ์ ๋ฐ๊ฟ ์ ์์ต๋๋ค. ์ด ์น์ ์์๋ ์ด ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.
The JavaBeanValueResolver
"get/is" ์ ๋์ฌ๊ฐ ๋ถ์ public ๋ฉ์๋์ ๊ฐ์ ํ์ธํฉ๋๋ค.
Context context = Context
.newBuilder(model)
.resolver(JavaBeanValueResolver.INSTANCE)
.build();
The FieldValueResolver
no-static ํ๋์ ๊ฐ์ ํ์ธํฉ๋๋ค.
Context context = Context
.newBuilder(model)
.resolver(FieldValueResolver.INSTANCE)
.build();
The MapValueResolver
java.util.Map
๊ฐ์ฒด์ ๊ฐ์ ํ์ธํฉ๋๋ค.
Context context = Context
.newBuilder(model)
.resolver(MapValueResolver.INSTANCE)
.build();
The MethodValueResolver
public ๋ฉ์๋์ ๊ฐ์ ํ์ธํฉ๋๋ค.
Context context = Context
.newBuilder(model)
.resolver(MethodValueResolver.INSTANCE)
.build();
The JsonNodeValueResolver
'JsonNode' ๊ฐ์ฒด์ ๊ฐ์ ํ์ธํฉ๋๋ค.
Context context = Context
.newBuilder(model)
.resolver(JsonNodeValueResolver.INSTANCE)
.build();
Jackson 1.x๊ณผ Jackson 2.x ๋ชจ๋์์ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
Multiples value resolvers ์ฌ์ฉ
Context context = Context
.newBuilder(model)
.resolver(
MapValueResolver.INSTANCE,
JavaBeanValueResolver.INSTANCE,
FieldValueResolver.INSTANCE
).build();
The Cache System
Cache System์ ํ์ฅ์ฑ๊ณผ ์ ์ฐ์ฑ์ ์ ๊ณตํ๋๋ก ์ค๊ณ๋์์ต๋๋ค. ๋ค์์ TemplateCache
์์คํ
์ ๋ํ ๊ฐ๋ตํ ๋ณด๊ธฐ์
๋๋ค.
public interface TemplateCache {
/**
* ์บ์์์ ๋ชจ๋ ๋งคํ์ ์ ๊ฑฐํฉ๋๋ค.
*/
void clear();
/**
* ์ด ์บ์๊ฐ ์๋ ๊ฒฝ์ฐ ์ด ์์ค์ ๋ํ ๋งคํ์ ์ ๊ฑฐํฉ๋๋ค.
*
* @param source ๋งคํ์ด ์บ์์์ ์ ๊ฑฐ๋ ์์ค
*/
void evict(TemplateSource source);
/**
* ์ด ์บ์๊ฐ ์ง์ ๋ ํค๋ฅผ ๋งคํํ๋ ๊ฐ์ ๋ฐํํฉ๋๋ค.
*
* @param source ์ฐ๊ฒฐ๋ ํ
ํ๋ฆฟ์ด ๋ฐํ๋ ์์ค์
๋๋ค.
* @param parser Handlebars ํ์
* @return A template.
* @throws IOException input์ด ํ์ฑ๋์ง ์๋ ๊ฒฝ์ฐ
*/
Template get(TemplateSource source, Parser parser) throws IOException;
}
๋ณด์๋ค์ํผ put
๋ฉ์๋๊ฐ ์์ต๋๋ค. ๋ชจ๋ ๋ฌด๊ฑฐ์ด ์์
์ ๊ธฐ๋ณธ์ ์ผ๋ก ์บ์ ์์คํ
์ ํต์ฌ์ธ get
๋ฉ์๋์์ ์ํ๋ฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก Handlebars.java๋ ๋ค์๊ณผ ๊ฐ์ null
์บ์ ๊ตฌํ(์ผ๋ช
no cache at all)์ ์ฌ์ฉํฉ๋๋ค.
Template get(TemplateSource source, Parser parser) throws IOException {
return parser.parse(source);
}
'null' ์บ์ ์ธ์๋ Handlebars.java๋ ์ธ ๊ฐ์ง ๊ตฌํ์ ๋ ์ ๊ณตํฉ๋๋ค:
ConcurrentMapTemplateCache
: ํ์ผ์ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ๊ฐ์งํ๋ConcurrentMap
์์ ๊ตฌ์ถ๋ ํ ํ๋ฆฟ ์บ์ ๊ตฌํ์ ๋๋ค.
์ด ๊ตฌํ์ ์ผ๋ฐ์ ์ผ๋ก ๋งค์ฐ ์ ์๋ํ์ง๋ง ๋ ์ด์์ ์ค๋ ๋๊ฐ ๋์ผํ ํ ํ๋ฆฟ์ ์ปดํ์ผํ ์ ์๋ ์์ ์ฌ์ง๊ฐ ์์ต๋๋ค. ์ปดํ์ผ๋ฌ๊ฐ ๋งค์ฐ ๋น ๋ฅด๊ธฐ ๋๋ฌธ์ Handlebars.java์์ ํฐ ๋ฌธ์ ๊ฐ ๋์ง ์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ด ๊ตฌํ์ ์ํ์ง ์๋๋ค๋ฉดHighConcurrencyTemplateCache
ํ ํ๋ฆฟ ์บ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.HighConcurrencyTemplateCache
: ํ์ผ์ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ๊ฐ์งํ๋ConcurrentMap
์์ ๊ตฌ์ถ๋ ํ ํ๋ฆฟ ์บ์ ๊ตฌํ์ ๋๋ค.
์ด ์บ์ ๊ตฌํ์ConcurrentMapTemplateCache
์ ์ํด ์์ฑ๋ ์ฐฝ์0
์ผ๋ก ์ ๊ฑฐํฉ๋๋ค.
์ด๊ฒ์ Java Concurrency in Practice์์ ์ค๋ช ํ ํจํด์ ๋ฐ๋ฅด๋ฉฐ ์ค๋ ๋ ์์ ๊ด๊ณ์์ด ํ ํ๋ฆฟ์ด ํ ๋ฒ๋ง ์ปดํ์ผ๋๋๋ก ํฉ๋๋ค.GuavaTemplateCache
: Google Guava๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋ ํ ํ๋ฆฟ ์บ์ ๊ตฌํ์ ๋๋ค. handlebars-guava-cache module์์ ์ฌ์ฉ ๊ฐ๋ฅ ํฉ๋๋ค.
๋ค์์ ํตํด ์บ์๋ฅผ ์ฌ์ฉํ๋๋ก Handlebars.java๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค:
Handlebars hbs = new Handlebars()
.with(new MyCache());
MissingValueResolver ์ฌ์ฉ (@deprecated)
NOTE: MissingValueResolver๋ <= 1.3.0
์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. > 1.3.0
์ ๊ฒฝ์ฐ helper ๋๋ฝ์ ์ฌ์ฉํ์ธ์.
MissingValueResolver
๋ฅผ ์ฌ์ฉํ๋ฉด null
๋ก ํ์ธ๋ {{๋ณ์}}
ํํ์์ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
MissingValueResolver missingValueResolver = new MissingValueResolver() {
public String resolve(Object context, String name) {
//๊ธฐ๋ณธ๊ฐ์ ๋ฐํํ๊ฑฐ๋ ์์ธ๋ฅผ ๋์ง๋๋ค.
...;
}
};
Handlebars handlebars = new Handlebars().with(missingValueResolver);
Helper Missing
๊ธฐ๋ณธ์ ์ผ๋ก Handlebars.java๋ helper๋ฅผ ํ์ธํ ์ ์๋ ๊ฒฝ์ฐ java.lang.IllegalArgumentException()
์ ๋ฐ์์ํต๋๋ค.
special helper์ธ helperMissing
์ ์ ๊ณตํ์ฌ ๊ธฐ๋ณธ ๋์์ ์ฌ์ ์ํ ์ ์์ต๋๋ค. ์์:
handlebars.registerHelperMissing(new Helper<Object>() {
@Override
public CharSequence apply(final Object context, final Options options) throws IOException {
return options.fn.text();
}
});
๋ฌธ์์ด ํ์ ๋งค๊ฐ๋ณ์
stringParams: true
๋ฅผ ์ค์ ํ๋ฉด ๋งค๊ฐ๋ณ์ ์ด๋ฆ์ ์ก์ธ์คํ ์ ์์ต๋๋ค. ์์:
{{sayHi this edgar}}
Handlebars handlebars = new Handlebars()
.stringParams(true);
handlebars.registerHelper("sayHi", new Helper<Object>() {
public Object apply(Object context, Options options) {
return "Hello " + options.param(0) + "!";
}
});
๊ฒฐ๊ณผ:
Hello edgar!
์ด๋ป๊ฒ ์๋ํฉ๋๊น? stringParams: true
๋ ๊ฐ์ด ์ปจํ
์คํธ ์คํ์ ์์ผ๋ฉด ์ด๋ฆ์ ๋ํ ๋งค๊ฐ๋ณ์๋ฅผ ํ์ธํ๋๋ก Handlebars.java์ ์ง์ํฉ๋๋ค.
๋ฌดํ ๋ฃจํ ํ์ฉ
๊ธฐ๋ณธ์ ์ผ๋ก Handlebars.java๋ ๋ถ๋ถ ํธ์ถ์ด ์ง์ ๋๋ ๊ฐ์ ์ ์ผ๋ก ํ์ฉ๋์ง ์์ต๋๋ค.Handlebars.inifiteLoops(true)
๋ฅผ ์ค์ ํ์ฌ ์ด๋ฅผ ๋ณ๊ฒฝํ ์ ์์ง๋ง StackOverflowError
์ ์ฃผ์ํ์ธ์.
Pretty Print
Mustache Spec์๋ ๊ณต๋ฐฑ๊ณผ ์ ์ค์ ์ ๊ฑฐํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ๊ท์น์ด ์์ต๋๋ค. ์ด ๊ธฐ๋ฅ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ๋์ด ์์ต๋๋ค.Handlebars.prettyPrint(true)
๋ฅผ ์ค์ ํ์ฌ ์ด ๊ธฐ๋ฅ์ ์ผค ์ ์์ต๋๋ค.
Modules
Jackson 1.x
Maven:
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-json</artifactId>
<version>${handlebars-version}</version>
</dependency>
Usage:
handlebars.registerHelper("json", JacksonHelper.INSTANCE);
{{json context [view="foo.MyFullyQualifiedClassName"] [escapeHTML=false] [pretty=false]}}
๋์:
handlebars.registerHelper("json", new JacksonHelper().viewAlias("myView",
foo.MyFullyQualifiedClassName.class);
{{json context [view="myView"] [escapeHTML=false] [pretty=false]}}
context: ๊ฐ์ฒด๋ null์ผ ์ ์์ต๋๋ค.
view: Jackson View์ ์ด๋ฆ์ด๋ฉฐ ์ ํ ์ฌํญ์ ๋๋ค.
escapeHTML: True, JSON ์ฝํ ์ธ ์ HTML ๋ฌธ์๊ฐ ํฌํจ๋์ด ์๊ณ ์ด ๋ฌธ์๋ฅผ ์ด์ค์ผ์ดํํด์ผ ํ๋ ๊ฒฝ์ฐ. ๊ธฐ๋ณธ๊ฐ: false.
pretty: True, JSON ์ฝํ ์ธ ์ ํ์์ ์ง์ ํด์ผ ํ๋ ๊ฒฝ์ฐ. ๊ธฐ๋ณธ๊ฐ: false.
Jackson 2.x
Maven:
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-jackson2</artifactId>
<version>${handlebars-version}</version>
</dependency>
helper ์ด๋ฆ์ ์ ์ธํ๊ณ Jackson1.x์ ๋์ผ: Jackson2Helper
Markdown
Maven:
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-markdown</artifactId>
<version>${handlebars-version}</version>
</dependency>
Usage:
handlebars.registerHelper("md", new MarkdownHelper());
{{md context}}
context: ํ์์ ์ผ๋ก ๊ฐ์ฒด ๋๋ null ์ ๋๋ค.
Humanize
Maven:
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-humanize</artifactId>
<version>${handlebars-version}</version>
</dependency>
Usage:
// Humanize helper๋ฅผ ๋ชจ๋ ๋ฑ๋ก
HumanizeHelper.register(handlebars);
์ถ๊ฐ ์ ๋ณด๋ [HumanizeHelper] (https://github.com/jknack/handlebars.java/blob/master/handlebars-humanize/src/main/java/com/github/jknack/handlebars/HumanizeHelper.java)์ JavaDoc์ ์ฐธ์กฐํ์ธ์.
SpringMVC
Maven:
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-springmvc</artifactId>
<version>${handlebars-version}</version>
</dependency>
Value resolvers ์ฌ์ฉ:
HandlebarsViewResolver viewResolver = ...;
viewResolver.setValueResolvers(...);
๋ํ HandlebarsViewResolver๋ Spring์ MessageSource
ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ message
helper๋ฅผ ์ถ๊ฐํฉ๋๋ค.
{{message "code" [arg]* [default="default message"]}}
์ค๋ช :
- code: ํ์์ธ ๋ฉ์์ง์ ์ฝ๋
- arg: ์ ํ ์ฌํญ์ธ ๋ฉ์์ง์ ์ธ์
- default: ์ ํ ์ฌํญ์ธ ๊ธฐ๋ณธ๊ฐ์ ๋ฉ์์ง
HandlebarsViewResolver๋ฅผ ํ์ธํ์ธ์.
์ฑ๋ฅ
Handlebars.java๋ ํ๋์ ์ด๊ณ ์์ ํ ๊ธฐ๋ฅ์ ๊ฐ์ถ ํ ํ๋ฆฟ ์์ง์ด์ง๋ง ๋งค์ฐ ์ฐ์ํ ์ฑ๋ฅ(Hbs)๋ ๊ฐ์ง๊ณ ์์ต๋๋ค.
๋ฒค์น๋งํฌ ์์ค ์ฝ๋๋ ์ฌ๊ธฐ์ ๋๋ค: https://github.com/mbosecke/template-benchmark
์ํคํ ์ฒ ๋ฐ API ์ค๊ณ
- Handlebars.java follows the JavaScript API with some minors exceptions due to the nature of the Java language.
- Handlebars.java๋ Java ์ธ์ด์ ํน์ฑ์ผ๋ก ์ธํด JavaScript API์ ์ผ๋ถ minors exceptions๋ฅผ ๋ฐ๋ฆ ๋๋ค.
- ํ์๋ ANTLR v4 ์์ ๊ตฌ์ถ๋ฉ๋๋ค.
- ๋ฐ์ดํฐ๋ ๊ธฐ๋ณธ ์ ํ(int, boolean, double ๋ฑ), ๋ฌธ์์ด, ๋งต, ๋ชฉ๋ก ๋๋ JavaBeans ๊ฐ์ฒด๋ก ์ ๊ณต๋ฉ๋๋ค.
- Helper๋ type-safe ์ ๋๋ค.
- Handlebars.java๋ thread-safe ์ ๋๋ค.
Handlebars.java์ Handlebars.js์ ์ฐจ์ด์
Handlebars.java์ scope resolution์ Mustache Spec์ ๋ฐ๋ฆ ๋๋ค. ์๋ฅผ ๋ค์ด:
Given:
{
"value": "parent",
"child": {}
}
and
Hello {{#child}}{{value}}{{/child}}
will be:
Hello parent
์ด์ Handlebars.js๊ฐ ์๋ ๋์ผํ ๋ชจ๋ธ ๋ฐ ํ ํ๋ฆฟ์:
Hello
์ด๋ Handlebars.js๊ฐ ์ปจํ ์คํธ ์คํ์์ current scope์์ ๋๋ฝ๋ ์์ฑ์ ์ฐพ์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค(์ด๋ Mustache ์ฌ์๊ณผ ์ผ์นํจ).
this.
๋ก ์์ฑ์ ํ์ ํ์ฌ Handlebars.java์์ ์ปจํ
์คํธ ์คํ lookup์ ํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค:
Hello {{#child}}{{this.value}}{{/child}}
Handlebars.java์ Mustache.js์ ์ฐจ์ด์
- partial์ โโ๋ก๋ํ ์ ์๋ ๊ฒฝ์ฐ Handlebars.java์์
java.io.FileNotFoundException
์ด ๋ฐ์ํฉ๋๋ค.
Status
Mustache 1.0 Compliant
- Mustache Spec์ 123๊ฐ ํ ์คํธ ํต๊ณผ
- ํ ์คํธ๋ ๋ค์ ๋ชฉ๋ก์์ ํ์ธํ ์ ์์ต๋๋ค. comments.yml, delimiters.yml, interpolation.yml, inverted.yml, lambdas.yml, partials.yml, sections.yml
Handlebars.js Compliant
- Handlebars.js tests์ ๋ชจ๋ ํ ์คํธ ํต๊ณผ
- ํ ์คํธ๋ ๋ค์ ๋ชฉ๋ก์์ ํ์ธํ ์ ์์ต๋๋ค. basic context, string literals, inverted sections, blocks, block helper missing, helper hash, partials
Dependencies
+- org.slf4j:slf4j-api:jar:1.6.4
FAQ
Contribute๋ฅผ ํ๊ณ ์ถ์ผ์ ๊ฐ์?
- ์ด project๋ฅผ Fork ํฉ๋๋ค.
- ์ด๋ค ์์ ์ ํด์ผ ํ ์ง ๊ถ๊ธํ์ธ์? ์์ /๋ฒ๊ทธ ๋ชฉ๋ก์ ๋ณด๊ณ ์์ ํ๊ณ ์ถ์ ๊ฒ์ ์ ํํ์ญ์์ค.
- ํ๋ ์ด์์ helper๋ฅผ donate ํ์๊ฒ ์ต๋๊น? ์ปค๋ฎค๋ํฐ helper๋ฅผ ์ํ ์ ์ฅ์ handlebars=helpers๋ฅผ ์ฐธ์กฐํ์ธ์.
- issue๋ fix๋ issues list์์ ๋ง๋์ธ์.
- mailing list์ ๊ฒ์๋ ์ง๋ฌธ์ ๋ํ ๋ต๋ณ์ ์๊ณ ์๋ค๋ฉด ์ฃผ์ ํ์ง ๋ง๊ณ ๋ต์ฅ์ ์์ฑํ์ธ์.
- mailing list์์ ์์ด๋์ด๋ฅผ ๊ณต์ ํ๊ฑฐ๋ ์ง๋ฌธ์ ํ์ธ์. ์ฃผ์ ํ์ง ๋ง๊ณ ๋ต์ฅ์ ์์ฑํ์ธ์. ๊ทธ๋ฌ๋ฉด javadocs/FAQ๋ฅผ ๊ฐ์ ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
- ํน์ ๊ธฐ๋ฅ์ ๋์น๋ฉด mailing list๋ฅผ ์ฐพ์๋ณด๊ฑฐ๋ ๋ฌธ์ํ์ธ์. ์ฃผ์ ํ์ง ๋ง๊ณ ๋ต์ฅ์ ์์ฑํ๊ณ ์ํ ์ฝ๋๋ฅผ ๋ณด์ฌ์ฃผ์ธ์. ๋ฌธ์ ๋ฅผ ์ค๋ช ํฉ๋๋ค.
- handlebars.java๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ํ์ฅํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ์ ์์ฑํ์ญ์์ค.
- ๋ถ๋ถ๋ช ํ ๊ฒ์ ๋ฐ๊ฒฌํ๋ฉด javadoc/์์ธ ๋ฉ์์ง์ ๋ํ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ํ์ญ์์ค.
- ๋ฌธ์์ ๋ฌธ์ ๊ฐ ์๋ ๊ฒฝ์ฐ ์ง๊ด์ ์ด์ง ์๊ฑฐ๋ ๋ฐ๋ฅด๊ธฐ ์ด๋ ต์ต๋๋ค. - ์๋ ค์ฃผ์๋ฉด ์ ์์ ๋ฐ๋ผ ๊ฐ์ ํ๋๋ก ๋ ธ๋ ฅํ๊ฒ ์ต๋๋ค. ์ด๋ค ๊ฑด์ค์ ์ธ ๋นํ๋ ๋งค์ฐ ๊ฐ์ฌ๋๋ฆฝ๋๋ค. ์ด๊ฒ์ ์ฌ๊ฐ ์๊ฐ์ ๊ฐ๋ฐ๋๊ณ ๋ฌธ์ํ๋ ์คํ ์์ค ํ๋ก์ ํธ๋ผ๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค.
๋์๋ง ๋ฐ ์ง์
๊ธฐ๋ถ
Edgar ๋งฅ์ฃผ๋ฅผ ์ฌ์ธ์!
๊ด๋ จ ํ๋ก์ ํธ
์ ์์
๋ผ์ด์ผ์ค
์ถ์ฒ : jknack/handlebars.java