Skip to content

Commit 258e876

Browse files
committed
Rework the syntax to use brackets instead of a $
1 parent efc5ad3 commit 258e876

File tree

4 files changed

+46
-20
lines changed

4 files changed

+46
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
[Yonas Kolb](https://github.com/yonaskolb)
99
[#214](https://github.com/stencilproject/Stencil/pull/214)
1010
- Variables now support indirect evaluation. For example, if you have a variable `key = "name"`, and an
11-
object `item = ["name": "John"]`, then `{{ item.$key }}` will evaluate to "John".
11+
object `item = ["name": "John"]`, then `{{ item[key] }}` will evaluate to "John".
1212
[David Jennes](https://github.com/djbe)
1313
[#215](https://github.com/stencilproject/Stencil/pull/215)
1414

Sources/Variable.swift

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,41 @@ public struct Variable : Equatable, Resolvable {
5050
self.variable = variable
5151
}
5252

53-
fileprivate func lookup() -> [String] {
54-
return variable.characters.split(separator: ".").map(String.init)
53+
// Split the lookup string and resolve references if possible
54+
fileprivate func lookup(_ context: Context) throws -> [String] {
55+
var current = ""
56+
var referenceLevel = 0
57+
58+
var bits = [String]()
59+
for c in variable {
60+
switch c {
61+
case "." where referenceLevel == 0:
62+
bits += [current]
63+
current = ""
64+
case "[" where referenceLevel == 0:
65+
bits += [current]
66+
current = ""
67+
referenceLevel += 1
68+
case "[":
69+
referenceLevel += 1
70+
current.append(c)
71+
case "]" where referenceLevel > 1:
72+
current.append(c)
73+
referenceLevel -= 1
74+
case "]":
75+
referenceLevel -= 1
76+
let value = try Variable(current)
77+
.resolve(context)
78+
.flatMap { "\($0)" }
79+
bits += [value ?? current]
80+
current = ""
81+
default:
82+
current.append(c)
83+
}
84+
}
85+
bits += [current]
86+
87+
return bits.filter { !$0.isEmpty }
5588
}
5689

5790
/// Resolve the variable in the given context
@@ -75,16 +108,9 @@ public struct Variable : Equatable, Resolvable {
75108
return bool
76109
}
77110

78-
for var bit in lookup() {
111+
for bit in try lookup(context) {
79112
current = normalize(current)
80113

81-
// try to evaluate bit if it's an indirection
82-
if bit.hasPrefix("$"),
83-
let resolved = try? Variable(String(bit.dropFirst())).resolve(context),
84-
let property = resolved {
85-
bit = "\(property)"
86-
}
87-
88114
if let context = current as? Context {
89115
current = context[bit]
90116
} else if let dictionary = current as? [String: Any] {

Tests/StencilTests/VariableSpec.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,31 +192,31 @@ func testVariable() {
192192
$0.describe("Indirection") {
193193
$0.it("can resolve an indirect variable") {
194194
try context.push(dictionary: ["property": "name"]) {
195-
let variable = Variable("$property")
195+
let variable = Variable("[property]")
196196
let result = try variable.resolve(context) as? String
197197
try expect(result) == "Kyle"
198198
}
199199
}
200200

201201
$0.it("can resolve an indirect variable via reflection") {
202202
try context.push(dictionary: ["property": "name"]) {
203-
let variable = Variable("article.author.$property")
203+
let variable = Variable("article.author[property]")
204204
let result = try variable.resolve(context) as? String
205205
try expect(result) == "Kyle"
206206
}
207207
}
208208

209209
$0.it("can resolve an item from an array via it's indirect index") {
210210
try context.push(dictionary: ["property": 0]) {
211-
let variable = Variable("contacts.$property")
211+
let variable = Variable("contacts[property]")
212212
let result = try variable.resolve(context) as? String
213213
try expect(result) == "Katie"
214214
}
215215
}
216216

217217
$0.it("can resolve an item from an array via unknown index") {
218218
try context.push(dictionary: ["property": 5]) {
219-
let variable = Variable("contacts.$property")
219+
let variable = Variable("contacts[property]")
220220
let result = try variable.resolve(context) as? String
221221
try expect(result).to.beNil()
222222
}
@@ -225,7 +225,7 @@ func testVariable() {
225225
#if os(OSX)
226226
$0.it("can resolve an indirect variable via KVO") {
227227
try context.push(dictionary: ["property": "name"]) {
228-
let variable = Variable("object.$property")
228+
let variable = Variable("object[property]")
229229
let result = try variable.resolve(context) as? String
230230
try expect(result) == "Foo"
231231
}
@@ -234,7 +234,7 @@ func testVariable() {
234234

235235
$0.it("can resolve a value via reflection") {
236236
try context.push(dictionary: ["property": "articles"]) {
237-
let variable = Variable("blog.$property.0.author.name")
237+
let variable = Variable("blog[property].0.author.name")
238238
let result = try variable.resolve(context) as? String
239239
try expect(result) == "Kyle"
240240
}
@@ -246,7 +246,7 @@ func testVariable() {
246246
"prop2": 0,
247247
"prop3": "name"
248248
]) {
249-
let variable = Variable("blog.$prop1.$prop2.author.$prop3")
249+
let variable = Variable("blog[prop1][prop2].author[prop3]")
250250
let result = try variable.resolve(context) as? String
251251
try expect(result) == "Kyle"
252252
}

docs/templates.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ For example, if `people` was an array:
3131
There are {{ people.count }} people. {{ people.first }} is the first
3232
person, followed by {{ people.1 }}.
3333

34-
For indirect lookup, you can prefix a variable with `$`. That variable will be evaluated first, before the actual lookup will happen.
34+
For indirect lookup, you can surround a variable with brackets (`[]`). That variable will be evaluated first, before the actual lookup will happen.
3535

3636
For example, if you have the following context:
3737

@@ -46,7 +46,7 @@ For example, if you have the following context:
4646
4747
.. code-block:: html+django
4848

49-
The result of {{ item.$key }} will be the same as {{ item.name }}. It will first evaluate the result of {{ key }}, and only then evaluate the lookup expression.
49+
The result of {{ item[key] }} will be the same as {{ item.name }}. It will first evaluate the result of {{ key }}, and only then evaluate the lookup expression.
5050

5151
Filters
5252
~~~~~~~

0 commit comments

Comments
 (0)