Allow using collection accessors on strings (#245)

* allow using collection accessors on strings

* refactored resolving collection accessors

* refactored to fileprivate function

* Update Variable.swift

* Update templates.rst
This commit is contained in:
Ilya Puchka
2018-09-22 16:41:45 +03:00
committed by GitHub
parent df2e193891
commit d238c25eef
4 changed files with 109 additions and 47 deletions

View File

@@ -26,6 +26,10 @@
[Ilya Puchka](https://github.com/ilyapuchka) [Ilya Puchka](https://github.com/ilyapuchka)
[#243](https://github.com/stencilproject/Stencil/pull/243) [#243](https://github.com/stencilproject/Stencil/pull/243)
- Now you can access string characters by index or get string length the same was as if it was an array, i.e. `{{ 'string'.first }}`, `{{ 'string'.last }}`, `{{ 'string'.1 }}`, `{{ 'string'.count }}`.
[Ilya Puchka](https://github.com/ilyapuchka)
[#245](https://github.com/stencilproject/Stencil/pull/245)
### Internal Changes ### Internal Changes
- Updated the codebase to use Swift 4 features. - Updated the codebase to use Swift 4 features.

View File

@@ -87,19 +87,9 @@ public struct Variable : Equatable, Resolvable {
current = dictionary[bit] current = dictionary[bit]
} }
} else if let array = current as? [Any] { } else if let array = current as? [Any] {
if let index = Int(bit) { current = resolveCollection(array, bit: bit)
if index >= 0 && index < array.count { } else if let string = current as? String {
current = array[index] current = resolveCollection(string, bit: bit)
} else {
current = nil
}
} else if bit == "first" {
current = array.first
} else if bit == "last" {
current = array.last
} else if bit == "count" {
current = array.count
}
} else if let object = current as? NSObject { // NSKeyValueCoding } else if let object = current as? NSObject { // NSKeyValueCoding
#if os(Linux) #if os(Linux)
return nil return nil
@@ -128,6 +118,24 @@ public struct Variable : Equatable, Resolvable {
} }
} }
private func resolveCollection<T: Collection>(_ collection: T, bit: String) -> Any? {
if let index = Int(bit) {
if index >= 0 && index < collection.count {
return collection[collection.index(collection.startIndex, offsetBy: index)]
} else {
return nil
}
} else if bit == "first" {
return collection.first
} else if bit == "last" {
return collection[collection.index(collection.endIndex, offsetBy: -1)]
} else if bit == "count" {
return collection.count
} else {
return nil
}
}
/// A structure used to represet range of two integer values expressed as `from...to`. /// A structure used to represet range of two integer values expressed as `from...to`.
/// Values should be numbers (they will be converted to integers). /// Values should be numbers (they will be converted to integers).
/// Rendering this variable produces array from range `from...to`. /// Rendering this variable produces array from range `from...to`.

View File

@@ -86,13 +86,62 @@ func testVariable() {
try expect(result) == "Kyle" try expect(result) == "Kyle"
} }
$0.it("can resolve an item from a dictionary") { $0.context("given string") {
$0.it("can resolve an item via it's index") {
let variable = Variable("name.0")
let result = try variable.resolve(context) as? Character
try expect(result) == "K"
let variable1 = Variable("name.1")
let result1 = try variable1.resolve(context) as? Character
try expect(result1) == "y"
}
$0.it("can resolve an item via unknown index") {
let variable = Variable("name.5")
let result = try variable.resolve(context) as? Character
try expect(result).to.beNil()
let variable1 = Variable("name.-5")
let result1 = try variable1.resolve(context) as? Character
try expect(result1).to.beNil()
}
$0.it("can resolve the first item") {
let variable = Variable("name.first")
let result = try variable.resolve(context) as? Character
try expect(result) == "K"
}
$0.it("can resolve the last item") {
let variable = Variable("name.last")
let result = try variable.resolve(context) as? Character
try expect(result) == "e"
}
$0.it("can get the characters count") {
let variable = Variable("name.count")
let result = try variable.resolve(context) as? Int
try expect(result) == 4
}
}
$0.context("given dictionary") {
$0.it("can resolve an item") {
let variable = Variable("profiles.github") let variable = Variable("profiles.github")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String
try expect(result) == "kylef" try expect(result) == "kylef"
} }
$0.it("can resolve an item from an array via it's index") { $0.it("can get the count") {
let variable = Variable("profiles.count")
let result = try variable.resolve(context) as? Int
try expect(result) == 1
}
}
$0.context("given array") {
$0.it("can resolve an item via it's index") {
let variable = Variable("contacts.0") let variable = Variable("contacts.0")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String
try expect(result) == "Katie" try expect(result) == "Katie"
@@ -102,7 +151,7 @@ func testVariable() {
try expect(result1) == "Carlton" try expect(result1) == "Carlton"
} }
$0.it("can resolve an item from an array via unknown index") { $0.it("can resolve an item via unknown index") {
let variable = Variable("contacts.5") let variable = Variable("contacts.5")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String
try expect(result).to.beNil() try expect(result).to.beNil()
@@ -112,30 +161,31 @@ func testVariable() {
try expect(result1).to.beNil() try expect(result1).to.beNil()
} }
$0.it("can resolve the first item from an array") { $0.it("can resolve the first item") {
let variable = Variable("contacts.first") let variable = Variable("contacts.first")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String
try expect(result) == "Katie" try expect(result) == "Katie"
} }
$0.it("can resolve the last item from an array") { $0.it("can resolve the last item") {
let variable = Variable("contacts.last") let variable = Variable("contacts.last")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String
try expect(result) == "Carlton" try expect(result) == "Carlton"
} }
$0.it("can get the count") {
let variable = Variable("contacts.count")
let result = try variable.resolve(context) as? Int
try expect(result) == 2
}
}
$0.it("can resolve a property with reflection") { $0.it("can resolve a property with reflection") {
let variable = Variable("article.author.name") let variable = Variable("article.author.name")
let result = try variable.resolve(context) as? String let result = try variable.resolve(context) as? String
try expect(result) == "Kyle" try expect(result) == "Kyle"
} }
$0.it("can get the count of a dictionary") {
let variable = Variable("profiles.count")
let result = try variable.resolve(context) as? Int
try expect(result) == 1
}
#if os(OSX) #if os(OSX)
$0.it("can resolve a value via KVO") { $0.it("can resolve a value via KVO") {
let variable = Variable("object.title") let variable = Variable("object.title")

View File

@@ -20,9 +20,9 @@ following lookup:
- Context lookup - Context lookup
- Dictionary lookup - Dictionary lookup
- Array lookup (first, last, count, index) - Array and string lookup (first, last, count, by index)
- Key value coding lookup - Key value coding lookup
- Type introspection - Type introspection (via ``Mirror``)
For example, if `people` was an array: For example, if `people` was an array: