forked from twitter/scrooge
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Handlebar.scala
84 lines (73 loc) · 2.8 KB
/
Handlebar.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/*
* Copyright 2011 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.twitter.handlebar
import scala.util.parsing.input.StreamReader
import java.io.StringReader
case class Unpacker[T](
document: AST.Document,
unpacker: T => Dictionary,
handlebar: Handlebar
) extends (T => String) {
def apply(item: T) = Handlebar.generate(document, unpacker(item))
}
object Handlebar {
import AST._
import Dictionary._
def generate(template: String, dictionary: Dictionary): String =
generate(Parser(StreamReader(new StringReader(template))), dictionary)
def generate(template: StreamReader, dictionary: Dictionary): String =
generate(Parser(template), dictionary)
def generate(document: Document, dictionary: Dictionary): String = {
document.segments.map { segment => process(segment, dictionary) }.mkString
}
private[this] def process(segment: Segment, dictionary: Dictionary): String = {
segment match {
case Data(data) => data
case Interpolation(name) => dictionary(name).toData
case Section(name, document, reversed, joiner) => {
dictionary(name) match {
case ListValue(items) => {
if (reversed) "" else items.map { d => generate(document, d) }.mkString(joiner.getOrElse(""))
}
case other => {
val expose = if (reversed) !other.toBoolean else other.toBoolean
if (expose) generate(document, dictionary) else ""
}
}
}
case Partial(name) => {
dictionary(name) match {
case PartialValue(handlebar) => handlebar.generate(dictionary)
case other => ""
}
}
}
}
}
case class Handlebar(document: AST.Document) {
def this(template: StreamReader) = this(Parser(template))
def this(template: String) = this(StreamReader(new StringReader(template)))
/**
* Create a string out of a template, using a dictionary to fill in the blanks.
*/
def generate(dictionary: Dictionary) = Handlebar.generate(document, dictionary)
/**
* Given an `unpacker` function that can turn objects of type `T` into dictionaries, return a
* new function of `T => String` that unpacks items of type `T` and runs them through the
* template.
*/
def generate[T](unpacker: T => Dictionary) = new Unpacker[T](document, unpacker, this)
}