Add support for Swift 3.0
This commit is contained in:
@@ -1 +1 @@
|
||||
2.2
|
||||
3.0-GM-CANDIDATE
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
os:
|
||||
- osx
|
||||
env:
|
||||
- SWIFT_VERSION=2.2
|
||||
language: generic
|
||||
sudo: required
|
||||
dist: trusty
|
||||
osx_image: xcode7.3
|
||||
osx_image: xcode8
|
||||
install:
|
||||
- eval "$(curl -sL https://gist.githubusercontent.com/kylef/5c0475ff02b7c7671d2a/raw/9f442512a46d7a2af7b850d65a7e9bd31edfb09b/swiftenv-install.sh)"
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo python -m ensurepip; fi
|
||||
- sudo pip install swim
|
||||
script:
|
||||
- swim test
|
||||
- swift test
|
||||
|
||||
6
CHANGELOG.md
Normal file
6
CHANGELOG.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Stencil Changelog
|
||||
## Master
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Adds support for Swift 3.0.
|
||||
@@ -3,9 +3,9 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "Stencil",
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/kylef/PathKit.git", majorVersion: 0, minor: 6),
|
||||
],
|
||||
testDependencies: [
|
||||
.Package(url: "https://github.com/kylef/Spectre.git", majorVersion: 0),
|
||||
.Package(url: "https://github.com/kylef/PathKit.git", majorVersion: 0, minor: 7),
|
||||
|
||||
// https://github.com/apple/swift-package-manager/pull/597
|
||||
.Package(url: "https://github.com/kylef/Spectre", majorVersion: 0, minor: 7),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -14,10 +14,10 @@ public class Context {
|
||||
self.namespace = namespace
|
||||
}
|
||||
|
||||
public subscript(key: String) -> Any? {
|
||||
open subscript(key: String) -> Any? {
|
||||
/// Retrieves a variable's value, starting at the current context and going upwards
|
||||
get {
|
||||
for dictionary in Array(dictionaries.reverse()) {
|
||||
for dictionary in Array(dictionaries.reversed()) {
|
||||
if let value = dictionary[key] {
|
||||
return value
|
||||
}
|
||||
@@ -37,19 +37,19 @@ public class Context {
|
||||
}
|
||||
|
||||
/// Push a new level into the Context
|
||||
private func push(dictionary: [String: Any]? = nil) {
|
||||
fileprivate func push(_ dictionary: [String: Any]? = nil) {
|
||||
dictionaries.append(dictionary ?? [:])
|
||||
}
|
||||
|
||||
/// Pop the last level off of the Context
|
||||
private func pop() -> [String: Any]? {
|
||||
fileprivate func pop() -> [String: Any]? {
|
||||
return dictionaries.popLast()
|
||||
}
|
||||
|
||||
/// Push a new level onto the context for the duration of the execution of the given closure
|
||||
public func push<Result>(dictionary: [String: Any]? = nil, @noescape closure: (() throws -> Result)) rethrows -> Result {
|
||||
open func push<Result>(dictionary: [String: Any]? = nil, closure: (() throws -> Result)) rethrows -> Result {
|
||||
push(dictionary)
|
||||
defer { pop() }
|
||||
defer { _ = pop() }
|
||||
return try closure()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
func toString(value: Any?) -> String? {
|
||||
func toString(_ value: Any?) -> String? {
|
||||
if let value = value as? String {
|
||||
return value
|
||||
} else if let value = value as? CustomStringConvertible {
|
||||
@@ -8,25 +8,25 @@ func toString(value: Any?) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func capitalise(value: Any?) -> Any? {
|
||||
func capitalise(_ value: Any?) -> Any? {
|
||||
if let value = toString(value) {
|
||||
return value.capitalizedString
|
||||
return value.capitalized
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func uppercase(value: Any?) -> Any? {
|
||||
func uppercase(_ value: Any?) -> Any? {
|
||||
if let value = toString(value) {
|
||||
return value.uppercaseString
|
||||
return value.uppercased()
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func lowercase(value: Any?) -> Any? {
|
||||
func lowercase(_ value: Any?) -> Any? {
|
||||
if let value = toString(value) {
|
||||
return value.lowercaseString
|
||||
return value.lowercased()
|
||||
}
|
||||
|
||||
return value
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
public class ForNode : NodeType {
|
||||
open class ForNode : NodeType {
|
||||
let variable:Variable
|
||||
let loopVariable:String
|
||||
let nodes:[NodeType]
|
||||
let emptyNodes: [NodeType]
|
||||
|
||||
public class func parse(parser:TokenParser, token:Token) throws -> NodeType {
|
||||
open class func parse(_ parser:TokenParser, token:Token) throws -> NodeType {
|
||||
let components = token.components()
|
||||
|
||||
guard components.count == 4 && components[2] == "in" else {
|
||||
@@ -24,7 +24,7 @@ public class ForNode : NodeType {
|
||||
|
||||
if token.contents == "empty" {
|
||||
emptyNodes = try parser.parse(until(["endfor"]))
|
||||
parser.nextToken()
|
||||
_ = parser.nextToken()
|
||||
}
|
||||
|
||||
return ForNode(variable: variable, loopVariable: loopVariable, nodes: forNodes, emptyNodes:emptyNodes)
|
||||
@@ -37,22 +37,22 @@ public class ForNode : NodeType {
|
||||
self.emptyNodes = emptyNodes
|
||||
}
|
||||
|
||||
public func render(context: Context) throws -> String {
|
||||
open func render(_ context: Context) throws -> String {
|
||||
let values = try variable.resolve(context)
|
||||
|
||||
if let values = values as? [Any] where values.count > 0 {
|
||||
if let values = values as? [Any] , values.count > 0 {
|
||||
let count = values.count
|
||||
return try values.enumerate().map { index, item in
|
||||
return try values.enumerated().map { index, item in
|
||||
let forContext: [String: Any] = [
|
||||
"first": index == 0,
|
||||
"last": index == (count - 1),
|
||||
"counter": index + 1,
|
||||
]
|
||||
|
||||
return try context.push([loopVariable: item, "forloop": forContext]) {
|
||||
return try context.push(dictionary: [loopVariable: item, "forloop": forContext]) {
|
||||
try renderNodes(nodes, context)
|
||||
}
|
||||
}.joinWithSeparator("")
|
||||
}.joined(separator: "")
|
||||
}
|
||||
|
||||
return try context.push {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
public class IfNode : NodeType {
|
||||
public let variable:Variable
|
||||
public let trueNodes:[NodeType]
|
||||
public let falseNodes:[NodeType]
|
||||
open class IfNode : NodeType {
|
||||
open let variable:Variable
|
||||
open let trueNodes:[NodeType]
|
||||
open let falseNodes:[NodeType]
|
||||
|
||||
public class func parse(parser:TokenParser, token:Token) throws -> NodeType {
|
||||
open class func parse(_ parser:TokenParser, token:Token) throws -> NodeType {
|
||||
let components = token.components()
|
||||
guard components.count == 2 else {
|
||||
throw TemplateSyntaxError("'if' statements should use the following 'if condition' `\(token.contents)`.")
|
||||
@@ -20,13 +20,13 @@ public class IfNode : NodeType {
|
||||
|
||||
if token.contents == "else" {
|
||||
falseNodes = try parser.parse(until(["endif"]))
|
||||
parser.nextToken()
|
||||
_ = parser.nextToken()
|
||||
}
|
||||
|
||||
return IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)
|
||||
}
|
||||
|
||||
public class func parse_ifnot(parser:TokenParser, token:Token) throws -> NodeType {
|
||||
open class func parse_ifnot(_ parser:TokenParser, token:Token) throws -> NodeType {
|
||||
let components = token.components()
|
||||
guard components.count == 2 else {
|
||||
throw TemplateSyntaxError("'ifnot' statements should use the following 'ifnot condition' `\(token.contents)`.")
|
||||
@@ -43,7 +43,7 @@ public class IfNode : NodeType {
|
||||
|
||||
if token.contents == "else" {
|
||||
trueNodes = try parser.parse(until(["endif"]))
|
||||
parser.nextToken()
|
||||
_ = parser.nextToken()
|
||||
}
|
||||
|
||||
return IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes)
|
||||
@@ -55,7 +55,7 @@ public class IfNode : NodeType {
|
||||
self.falseNodes = falseNodes
|
||||
}
|
||||
|
||||
public func render(context: Context) throws -> String {
|
||||
open func render(_ context: Context) throws -> String {
|
||||
let result = try variable.resolve(context)
|
||||
var truthy = false
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import PathKit
|
||||
|
||||
|
||||
public class IncludeNode : NodeType {
|
||||
public let templateName: Variable
|
||||
open class IncludeNode : NodeType {
|
||||
open let templateName: Variable
|
||||
|
||||
public class func parse(parser: TokenParser, token: Token) throws -> NodeType {
|
||||
open class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
|
||||
let bits = token.components()
|
||||
|
||||
guard bits.count == 2 else {
|
||||
@@ -18,7 +18,7 @@ public class IncludeNode : NodeType {
|
||||
self.templateName = templateName
|
||||
}
|
||||
|
||||
public func render(context: Context) throws -> String {
|
||||
open func render(_ context: Context) throws -> String {
|
||||
guard let loader = context["loader"] as? TemplateLoader else {
|
||||
throw TemplateSyntaxError("Template loader not in context")
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class IncludeNode : NodeType {
|
||||
}
|
||||
|
||||
guard let template = loader.loadTemplate(templateName) else {
|
||||
let paths = loader.paths.map { $0.description }.joinWithSeparator(", ")
|
||||
let paths = loader.paths.map { $0.description }.joined(separator: ", ")
|
||||
throw TemplateSyntaxError("'\(templateName)' template not found in \(paths)")
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ class BlockContext {
|
||||
self.blocks = blocks
|
||||
}
|
||||
|
||||
func pop(blockName: String) -> BlockNode? {
|
||||
return blocks.removeValueForKey(blockName)
|
||||
func pop(_ blockName: String) -> BlockNode? {
|
||||
return blocks.removeValue(forKey: blockName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension CollectionType {
|
||||
func any(closure: Generator.Element -> Bool) -> Generator.Element? {
|
||||
extension Collection {
|
||||
func any(_ closure: (Iterator.Element) -> Bool) -> Iterator.Element? {
|
||||
for element in self {
|
||||
if closure(element) {
|
||||
return element
|
||||
@@ -30,7 +30,7 @@ class ExtendsNode : NodeType {
|
||||
let templateName: Variable
|
||||
let blocks: [String:BlockNode]
|
||||
|
||||
class func parse(parser: TokenParser, token: Token) throws -> NodeType {
|
||||
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
|
||||
let bits = token.components()
|
||||
|
||||
guard bits.count == 2 else {
|
||||
@@ -59,7 +59,7 @@ class ExtendsNode : NodeType {
|
||||
self.blocks = blocks
|
||||
}
|
||||
|
||||
func render(context: Context) throws -> String {
|
||||
func render(_ context: Context) throws -> String {
|
||||
guard let loader = context["loader"] as? TemplateLoader else {
|
||||
throw TemplateSyntaxError("Template loader not in context")
|
||||
}
|
||||
@@ -69,12 +69,12 @@ class ExtendsNode : NodeType {
|
||||
}
|
||||
|
||||
guard let template = loader.loadTemplate(templateName) else {
|
||||
let paths:String = loader.paths.map { $0.description }.joinWithSeparator(", ")
|
||||
let paths:String = loader.paths.map { $0.description }.joined(separator: ", ")
|
||||
throw TemplateSyntaxError("'\(templateName)' template not found in \(paths)")
|
||||
}
|
||||
|
||||
let blockContext = BlockContext(blocks: blocks)
|
||||
return try context.push([BlockContext.contextKey: blockContext]) {
|
||||
return try context.push(dictionary: [BlockContext.contextKey: blockContext]) {
|
||||
return try template.render(context)
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class BlockNode : NodeType {
|
||||
let name: String
|
||||
let nodes: [NodeType]
|
||||
|
||||
class func parse(parser: TokenParser, token: Token) throws -> NodeType {
|
||||
class func parse(_ parser: TokenParser, token: Token) throws -> NodeType {
|
||||
let bits = token.components()
|
||||
|
||||
guard bits.count == 2 else {
|
||||
@@ -94,7 +94,7 @@ class BlockNode : NodeType {
|
||||
|
||||
let blockName = bits[1]
|
||||
let nodes = try parser.parse(until(["endblock"]))
|
||||
parser.nextToken()
|
||||
_ = parser.nextToken()
|
||||
return BlockNode(name:blockName, nodes:nodes)
|
||||
}
|
||||
|
||||
@@ -103,8 +103,8 @@ class BlockNode : NodeType {
|
||||
self.nodes = nodes
|
||||
}
|
||||
|
||||
func render(context: Context) throws -> String {
|
||||
if let blockContext = context[BlockContext.contextKey] as? BlockContext, node = blockContext.pop(name) {
|
||||
func render(_ context: Context) throws -> String {
|
||||
if let blockContext = context[BlockContext.contextKey] as? BlockContext, let node = blockContext.pop(name) {
|
||||
return try node.render(context)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,18 +7,20 @@ public struct Lexer {
|
||||
|
||||
func createToken(string:String) -> Token {
|
||||
func strip() -> String {
|
||||
return string[string.startIndex.successor().successor()..<string.endIndex.predecessor().predecessor()].trim(" ")
|
||||
let start = string.index(string.startIndex, offsetBy: 2)
|
||||
let end = string.index(string.endIndex, offsetBy: -2)
|
||||
return string[start..<end].trim(character: " ")
|
||||
}
|
||||
|
||||
if string.hasPrefix("{{") {
|
||||
return Token.Variable(value: strip())
|
||||
return .variable(value: strip())
|
||||
} else if string.hasPrefix("{%") {
|
||||
return Token.Block(value: strip())
|
||||
return .block(value: strip())
|
||||
} else if string.hasPrefix("{#") {
|
||||
return Token.Comment(value: strip())
|
||||
return .comment(value: strip())
|
||||
}
|
||||
|
||||
return Token.Text(value: string)
|
||||
return .text(value: string)
|
||||
}
|
||||
|
||||
/// Returns an array of tokens from a given template string.
|
||||
@@ -36,14 +38,14 @@ public struct Lexer {
|
||||
while !scanner.isEmpty {
|
||||
if let text = scanner.scan(until: ["{{", "{%", "{#"]) {
|
||||
if !text.1.isEmpty {
|
||||
tokens.append(createToken(text.1))
|
||||
tokens.append(createToken(string: text.1))
|
||||
}
|
||||
|
||||
let end = map[text.0]!
|
||||
let result = scanner.scan(until: end, returnUntil: true)
|
||||
tokens.append(createToken(result))
|
||||
tokens.append(createToken(string: result))
|
||||
} else {
|
||||
tokens.append(createToken(scanner.content))
|
||||
tokens.append(createToken(string: scanner.content))
|
||||
scanner.content = ""
|
||||
}
|
||||
}
|
||||
@@ -64,49 +66,50 @@ class Scanner {
|
||||
return content.isEmpty
|
||||
}
|
||||
|
||||
func scan(until until: String, returnUntil: Bool = false) -> String {
|
||||
func scan(until: String, returnUntil: Bool = false) -> String {
|
||||
if until.isEmpty {
|
||||
return ""
|
||||
}
|
||||
|
||||
var index = content.startIndex
|
||||
while index != content.endIndex {
|
||||
let substring = content.substringFromIndex(index)
|
||||
let substring = content.substring(from: index)
|
||||
|
||||
if substring.hasPrefix(until) {
|
||||
let result = content.substringToIndex(index)
|
||||
let result = content.substring(to: index)
|
||||
content = substring
|
||||
|
||||
if returnUntil {
|
||||
content = content.substringFromIndex(until.endIndex)
|
||||
content = content.substring(from: until.endIndex)
|
||||
return result + until
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
index = index.successor()
|
||||
index = content.index(after: index)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func scan(until until: [String]) -> (String, String)? {
|
||||
func scan(until: [String]) -> (String, String)? {
|
||||
if until.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var index = content.startIndex
|
||||
while index != content.endIndex {
|
||||
let substring = content.substringFromIndex(index)
|
||||
let substring = content.substring(from: index)
|
||||
for string in until {
|
||||
if substring.hasPrefix(string) {
|
||||
let result = content.substringToIndex(index)
|
||||
let result = content.substring(to: index)
|
||||
content = substring
|
||||
return (string, result)
|
||||
}
|
||||
}
|
||||
|
||||
index = index.successor()
|
||||
index = content.index(after: index)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -117,31 +120,33 @@ class Scanner {
|
||||
extension String {
|
||||
func findFirstNot(character: Character) -> String.Index? {
|
||||
var index = startIndex
|
||||
|
||||
while index != endIndex {
|
||||
if character != self[index] {
|
||||
return index
|
||||
}
|
||||
index = index.successor()
|
||||
index = self.index(after: index)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findLastNot(character: Character) -> String.Index? {
|
||||
var index = endIndex.predecessor()
|
||||
var index = self.index(before: endIndex)
|
||||
|
||||
while index != startIndex {
|
||||
if character != self[index] {
|
||||
return index.successor()
|
||||
return self.index(after: index)
|
||||
}
|
||||
index = index.predecessor()
|
||||
index = self.index(before: index)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func trim(character: Character) -> String {
|
||||
let first = findFirstNot(character) ?? startIndex
|
||||
let last = findLastNot(character) ?? endIndex
|
||||
let first = findFirstNot(character: character) ?? startIndex
|
||||
let last = findLastNot(character: character) ?? endIndex
|
||||
return self[first..<last]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
public class Namespace {
|
||||
open class Namespace {
|
||||
public typealias TagParser = (TokenParser, Token) throws -> NodeType
|
||||
|
||||
var tags = [String: TagParser]()
|
||||
@@ -9,7 +9,7 @@ public class Namespace {
|
||||
registerDefaultFilters()
|
||||
}
|
||||
|
||||
private func registerDefaultTags() {
|
||||
fileprivate func registerDefaultTags() {
|
||||
registerTag("for", parser: ForNode.parse)
|
||||
registerTag("if", parser: IfNode.parse)
|
||||
registerTag("ifnot", parser: IfNode.parse_ifnot)
|
||||
@@ -21,26 +21,26 @@ public class Namespace {
|
||||
registerTag("block", parser: BlockNode.parse)
|
||||
}
|
||||
|
||||
private func registerDefaultFilters() {
|
||||
fileprivate func registerDefaultFilters() {
|
||||
registerFilter("capitalize", filter: capitalise)
|
||||
registerFilter("uppercase", filter: uppercase)
|
||||
registerFilter("lowercase", filter: lowercase)
|
||||
}
|
||||
|
||||
/// Registers a new template tag
|
||||
public func registerTag(name: String, parser: TagParser) {
|
||||
open func registerTag(_ name: String, parser: @escaping TagParser) {
|
||||
tags[name] = parser
|
||||
}
|
||||
|
||||
/// Registers a simple template tag with a name and a handler
|
||||
public func registerSimpleTag(name: String, handler: Context throws -> String) {
|
||||
open func registerSimpleTag(_ name: String, handler: @escaping (Context) throws -> String) {
|
||||
registerTag(name, parser: { parser, token in
|
||||
return SimpleNode(handler: handler)
|
||||
})
|
||||
}
|
||||
|
||||
/// Registers a template filter with the given name
|
||||
public func registerFilter(name: String, filter: Filter) {
|
||||
open func registerFilter(_ name: String, filter: @escaping Filter) {
|
||||
filters[name] = filter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
public struct TemplateSyntaxError : ErrorType, Equatable, CustomStringConvertible {
|
||||
public struct TemplateSyntaxError : Error, Equatable, CustomStringConvertible {
|
||||
public let description:String
|
||||
|
||||
public init(_ description:String) {
|
||||
@@ -17,48 +17,48 @@ public func ==(lhs:TemplateSyntaxError, rhs:TemplateSyntaxError) -> Bool {
|
||||
|
||||
public protocol NodeType {
|
||||
/// Render the node in the given context
|
||||
func render(context:Context) throws -> String
|
||||
func render(_ context:Context) throws -> String
|
||||
}
|
||||
|
||||
|
||||
/// Render the collection of nodes in the given context
|
||||
public func renderNodes(nodes:[NodeType], _ context:Context) throws -> String {
|
||||
return try nodes.map { try $0.render(context) }.joinWithSeparator("")
|
||||
public func renderNodes(_ nodes:[NodeType], _ context:Context) throws -> String {
|
||||
return try nodes.map { try $0.render(context) }.joined(separator: "")
|
||||
}
|
||||
|
||||
public class SimpleNode : NodeType {
|
||||
let handler:Context throws -> String
|
||||
open class SimpleNode : NodeType {
|
||||
let handler:(Context) throws -> String
|
||||
|
||||
public init(handler:Context throws -> String) {
|
||||
public init(handler:@escaping (Context) throws -> String) {
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
public func render(context: Context) throws -> String {
|
||||
open func render(_ context: Context) throws -> String {
|
||||
return try handler(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TextNode : NodeType {
|
||||
public let text:String
|
||||
open class TextNode : NodeType {
|
||||
open let text:String
|
||||
|
||||
public init(text:String) {
|
||||
self.text = text
|
||||
}
|
||||
|
||||
public func render(context:Context) throws -> String {
|
||||
open func render(_ context:Context) throws -> String {
|
||||
return self.text
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol Resolvable {
|
||||
func resolve(context: Context) throws -> Any?
|
||||
func resolve(_ context: Context) throws -> Any?
|
||||
}
|
||||
|
||||
|
||||
public class VariableNode : NodeType {
|
||||
public let variable: Resolvable
|
||||
open class VariableNode : NodeType {
|
||||
open let variable: Resolvable
|
||||
|
||||
public init(variable: Resolvable) {
|
||||
self.variable = variable
|
||||
@@ -68,7 +68,7 @@ public class VariableNode : NodeType {
|
||||
self.variable = Variable(variable)
|
||||
}
|
||||
|
||||
public func render(context: Context) throws -> String {
|
||||
open func render(_ context: Context) throws -> String {
|
||||
let result = try variable.resolve(context)
|
||||
|
||||
if let result = result as? String {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
public class NowNode : NodeType {
|
||||
public let format:Variable
|
||||
open class NowNode : NodeType {
|
||||
open let format:Variable
|
||||
|
||||
public class func parse(parser:TokenParser, token:Token) throws -> NodeType {
|
||||
open class func parse(_ parser:TokenParser, token:Token) throws -> NodeType {
|
||||
var format:Variable?
|
||||
|
||||
let components = token.components()
|
||||
@@ -23,21 +23,21 @@ public class NowNode : NodeType {
|
||||
self.format = format ?? Variable("\"yyyy-MM-dd 'at' HH:mm\"")
|
||||
}
|
||||
|
||||
public func render(context: Context) throws -> String {
|
||||
let date = NSDate()
|
||||
open func render(_ context: Context) throws -> String {
|
||||
let date = Date()
|
||||
let format = try self.format.resolve(context)
|
||||
var formatter:NSDateFormatter?
|
||||
var formatter:DateFormatter?
|
||||
|
||||
if let format = format as? NSDateFormatter {
|
||||
if let format = format as? DateFormatter {
|
||||
formatter = format
|
||||
} else if let format = format as? String {
|
||||
formatter = NSDateFormatter()
|
||||
formatter = DateFormatter()
|
||||
formatter!.dateFormat = format
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
||||
return formatter!.stringFromDate(date)
|
||||
return formatter!.string(from: date)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
public func until(tags: [String]) -> ((TokenParser, Token) -> Bool) {
|
||||
public func until(_ tags: [String]) -> ((TokenParser, Token) -> Bool) {
|
||||
return { parser, token in
|
||||
if let name = token.components().first {
|
||||
for tag in tags {
|
||||
@@ -12,14 +12,14 @@ public func until(tags: [String]) -> ((TokenParser, Token) -> Bool) {
|
||||
}
|
||||
}
|
||||
|
||||
public typealias Filter = Any? throws -> Any?
|
||||
public typealias Filter = (Any?) throws -> Any?
|
||||
|
||||
/// A class for parsing an array of tokens and converts them into a collection of Node's
|
||||
public class TokenParser {
|
||||
open class TokenParser {
|
||||
public typealias TagParser = (TokenParser, Token) throws -> NodeType
|
||||
|
||||
private var tokens: [Token]
|
||||
private let namespace: Namespace
|
||||
fileprivate var tokens: [Token]
|
||||
fileprivate let namespace: Namespace
|
||||
|
||||
public init(tokens: [Token], namespace: Namespace) {
|
||||
self.tokens = tokens
|
||||
@@ -27,25 +27,25 @@ public class TokenParser {
|
||||
}
|
||||
|
||||
/// Parse the given tokens into nodes
|
||||
public func parse() throws -> [NodeType] {
|
||||
open func parse() throws -> [NodeType] {
|
||||
return try parse(nil)
|
||||
}
|
||||
|
||||
public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) throws -> [NodeType] {
|
||||
open func parse(_ parse_until:((_ parser:TokenParser, _ token:Token) -> (Bool))?) throws -> [NodeType] {
|
||||
var nodes = [NodeType]()
|
||||
|
||||
while tokens.count > 0 {
|
||||
let token = nextToken()!
|
||||
|
||||
switch token {
|
||||
case .Text(let text):
|
||||
case .text(let text):
|
||||
nodes.append(TextNode(text: text))
|
||||
case .Variable:
|
||||
case .variable:
|
||||
nodes.append(VariableNode(variable: try compileFilter(token.contents)))
|
||||
case .Block:
|
||||
case .block:
|
||||
let tag = token.components().first
|
||||
|
||||
if let parse_until = parse_until where parse_until(parser: self, token: token) {
|
||||
if let parse_until = parse_until , parse_until(self, token) {
|
||||
prependToken(token)
|
||||
return nodes
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class TokenParser {
|
||||
throw TemplateSyntaxError("Unknown template tag '\(tag)'")
|
||||
}
|
||||
}
|
||||
case .Comment:
|
||||
case .comment:
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -65,19 +65,19 @@ public class TokenParser {
|
||||
return nodes
|
||||
}
|
||||
|
||||
public func nextToken() -> Token? {
|
||||
open func nextToken() -> Token? {
|
||||
if tokens.count > 0 {
|
||||
return tokens.removeAtIndex(0)
|
||||
return tokens.remove(at: 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func prependToken(token:Token) {
|
||||
tokens.insert(token, atIndex: 0)
|
||||
open func prependToken(_ token:Token) {
|
||||
tokens.insert(token, at: 0)
|
||||
}
|
||||
|
||||
public func findFilter(name: String) throws -> Filter {
|
||||
open func findFilter(_ name: String) throws -> Filter {
|
||||
if let filter = namespace.filters[name] {
|
||||
return filter
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class TokenParser {
|
||||
throw TemplateSyntaxError("Invalid filter '\(name)'")
|
||||
}
|
||||
|
||||
func compileFilter(token: String) throws -> Resolvable {
|
||||
func compileFilter(_ token: String) throws -> Resolvable {
|
||||
return try FilterExpression(token: token, parser: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ let NSFileNoSuchFileError = 4
|
||||
#endif
|
||||
|
||||
/// A class representing a template
|
||||
public class Template {
|
||||
open class Template {
|
||||
let tokens: [Token]
|
||||
|
||||
/// Create a template with the given name inside the given bundle
|
||||
public convenience init(named:String, inBundle bundle:NSBundle? = nil) throws {
|
||||
let useBundle = bundle ?? NSBundle.mainBundle()
|
||||
guard let url = useBundle.URLForResource(named, withExtension: nil) else {
|
||||
public convenience init(named:String, inBundle bundle:Bundle? = nil) throws {
|
||||
let useBundle = bundle ?? Bundle.main
|
||||
guard let url = useBundle.url(forResource: named, withExtension: nil) else {
|
||||
throw NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError, userInfo: nil)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ public class Template {
|
||||
}
|
||||
|
||||
/// Create a template with a file found at the given URL
|
||||
public convenience init(URL:NSURL) throws {
|
||||
try self.init(path: Path(URL.path!))
|
||||
public convenience init(URL:Foundation.URL) throws {
|
||||
try self.init(path: Path(URL.path))
|
||||
}
|
||||
|
||||
/// Create a template with a file found at the given path
|
||||
@@ -36,7 +36,7 @@ public class Template {
|
||||
}
|
||||
|
||||
/// Render the given template
|
||||
public func render(context: Context? = nil) throws -> String {
|
||||
open func render(_ context: Context? = nil) throws -> String {
|
||||
let context = context ?? Context()
|
||||
let parser = TokenParser(tokens: tokens, namespace: context.namespace)
|
||||
let nodes = try parser.parse()
|
||||
|
||||
@@ -3,24 +3,24 @@ import PathKit
|
||||
|
||||
|
||||
// A class for loading a template from disk
|
||||
public class TemplateLoader {
|
||||
public let paths: [Path]
|
||||
open class TemplateLoader {
|
||||
open let paths: [Path]
|
||||
|
||||
public init(paths: [Path]) {
|
||||
self.paths = paths
|
||||
}
|
||||
|
||||
public init(bundle: [NSBundle]) {
|
||||
public init(bundle: [Bundle]) {
|
||||
self.paths = bundle.map {
|
||||
return Path($0.bundlePath)
|
||||
}
|
||||
}
|
||||
|
||||
public func loadTemplate(templateName: String) -> Template? {
|
||||
open func loadTemplate(_ templateName: String) -> Template? {
|
||||
return loadTemplate([templateName])
|
||||
}
|
||||
|
||||
public func loadTemplate(templateNames: [String]) -> Template? {
|
||||
open func loadTemplate(_ templateNames: [String]) -> Template? {
|
||||
for path in paths {
|
||||
for templateName in templateNames {
|
||||
let templatePath = path + Path(templateName)
|
||||
|
||||
@@ -2,7 +2,7 @@ import Foundation
|
||||
|
||||
|
||||
/// Split a string by spaces leaving quoted phrases together
|
||||
func smartSplit(value: String) -> [String] {
|
||||
func smartSplit(_ value: String) -> [String] {
|
||||
var word = ""
|
||||
var separator: Character = " "
|
||||
var components: [String] = []
|
||||
@@ -37,40 +37,40 @@ func smartSplit(value: String) -> [String] {
|
||||
|
||||
public enum Token : Equatable {
|
||||
/// A token representing a piece of text.
|
||||
case Text(value: String)
|
||||
case text(value: String)
|
||||
|
||||
/// A token representing a variable.
|
||||
case Variable(value: String)
|
||||
case variable(value: String)
|
||||
|
||||
/// A token representing a comment.
|
||||
case Comment(value: String)
|
||||
case comment(value: String)
|
||||
|
||||
/// A token representing a template block.
|
||||
case Block(value: String)
|
||||
case block(value: String)
|
||||
|
||||
/// Returns the underlying value as an array seperated by spaces
|
||||
public func components() -> [String] {
|
||||
switch self {
|
||||
case .Block(let value):
|
||||
case .block(let value):
|
||||
return smartSplit(value)
|
||||
case .Variable(let value):
|
||||
case .variable(let value):
|
||||
return smartSplit(value)
|
||||
case .Text(let value):
|
||||
case .text(let value):
|
||||
return smartSplit(value)
|
||||
case .Comment(let value):
|
||||
case .comment(let value):
|
||||
return smartSplit(value)
|
||||
}
|
||||
}
|
||||
|
||||
public var contents: String {
|
||||
switch self {
|
||||
case .Block(let value):
|
||||
case .block(let value):
|
||||
return value
|
||||
case .Variable(let value):
|
||||
case .variable(let value):
|
||||
return value
|
||||
case .Text(let value):
|
||||
case .text(let value):
|
||||
return value
|
||||
case .Comment(let value):
|
||||
case .comment(let value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -79,13 +79,13 @@ public enum Token : Equatable {
|
||||
|
||||
public func == (lhs: Token, rhs: Token) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.Text(let lhsValue), .Text(let rhsValue)):
|
||||
case (.text(let lhsValue), .text(let rhsValue)):
|
||||
return lhsValue == rhsValue
|
||||
case (.Variable(let lhsValue), .Variable(let rhsValue)):
|
||||
case (.variable(let lhsValue), .variable(let rhsValue)):
|
||||
return lhsValue == rhsValue
|
||||
case (.Block(let lhsValue), .Block(let rhsValue)):
|
||||
case (.block(let lhsValue), .block(let rhsValue)):
|
||||
return lhsValue == rhsValue
|
||||
case (.Comment(let lhsValue), .Comment(let rhsValue)):
|
||||
case (.comment(let lhsValue), .comment(let rhsValue)):
|
||||
return lhsValue == rhsValue
|
||||
default:
|
||||
return false
|
||||
|
||||
@@ -6,7 +6,7 @@ class FilterExpression : Resolvable {
|
||||
let variable: Variable
|
||||
|
||||
init(token: String, parser: TokenParser) throws {
|
||||
let bits = token.characters.split("|").map({ String($0).trim(" ") })
|
||||
let bits = token.characters.split(separator: "|").map({ String($0).trim(character: " ") })
|
||||
if bits.isEmpty {
|
||||
filters = []
|
||||
variable = Variable("")
|
||||
@@ -14,7 +14,7 @@ class FilterExpression : Resolvable {
|
||||
}
|
||||
|
||||
variable = Variable(bits[0])
|
||||
let filterBits = bits[1 ..< bits.endIndex]
|
||||
let filterBits = bits[bits.indices.suffix(from: 1)]
|
||||
|
||||
do {
|
||||
filters = try filterBits.map { try parser.findFilter($0) }
|
||||
@@ -24,7 +24,7 @@ class FilterExpression : Resolvable {
|
||||
}
|
||||
}
|
||||
|
||||
func resolve(context: Context) throws -> Any? {
|
||||
func resolve(_ context: Context) throws -> Any? {
|
||||
let result = try variable.resolve(context)
|
||||
|
||||
return try filters.reduce(result) { x, y in
|
||||
@@ -42,17 +42,17 @@ public struct Variable : Equatable, Resolvable {
|
||||
self.variable = variable
|
||||
}
|
||||
|
||||
private func lookup() -> [String] {
|
||||
return variable.characters.split(".").map(String.init)
|
||||
fileprivate func lookup() -> [String] {
|
||||
return variable.characters.split(separator: ".").map(String.init)
|
||||
}
|
||||
|
||||
/// Resolve the variable in the given context
|
||||
public func resolve(context: Context) throws -> Any? {
|
||||
public func resolve(_ context: Context) throws -> Any? {
|
||||
var current: Any? = context
|
||||
|
||||
if (variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\"")) {
|
||||
// String literal
|
||||
return variable[variable.startIndex.successor() ..< variable.endIndex.predecessor()]
|
||||
return variable[variable.characters.index(after: variable.startIndex) ..< variable.characters.index(before: variable.endIndex)]
|
||||
}
|
||||
|
||||
for bit in lookup() {
|
||||
@@ -76,7 +76,7 @@ public struct Variable : Equatable, Resolvable {
|
||||
#if os(Linux)
|
||||
return nil
|
||||
#else
|
||||
current = object.valueForKey(bit)
|
||||
current = object.value(forKey: bit)
|
||||
#endif
|
||||
} else {
|
||||
return nil
|
||||
@@ -92,7 +92,7 @@ public func ==(lhs: Variable, rhs: Variable) -> Bool {
|
||||
}
|
||||
|
||||
|
||||
func normalize(current: Any?) -> Any? {
|
||||
func normalize(_ current: Any?) -> Any? {
|
||||
if let current = current as? Normalizable {
|
||||
return current.normalize()
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
},
|
||||
"requires_arc": true,
|
||||
"dependencies": {
|
||||
"PathKit": [ "~> 0.6.0" ]
|
||||
"PathKit": [ "~> 0.7.0" ]
|
||||
}
|
||||
}
|
||||
|
||||
3
Tests/LinuxMain.swift
Normal file
3
Tests/LinuxMain.swift
Normal file
@@ -0,0 +1,3 @@
|
||||
import StencilTests
|
||||
|
||||
stencilTests()
|
||||
@@ -50,7 +50,7 @@ func testContext() {
|
||||
$0.it("allows you to push a dictionary and run a closure then restoring previous state") {
|
||||
var didRun = false
|
||||
|
||||
try context.push(["name": "Katie"]) {
|
||||
try context.push(dictionary: ["name": "Katie"]) {
|
||||
didRun = true
|
||||
try expect(context["name"] as? String) == "Katie"
|
||||
}
|
||||
@@ -6,12 +6,12 @@ func testIfNode() {
|
||||
describe("IfNode") {
|
||||
$0.describe("parsing") {
|
||||
$0.it("can parse an if block") {
|
||||
let tokens = [
|
||||
Token.Block(value: "if value"),
|
||||
Token.Text(value: "true"),
|
||||
Token.Block(value: "else"),
|
||||
Token.Text(value: "false"),
|
||||
Token.Block(value: "endif")
|
||||
let tokens: [Token] = [
|
||||
.block(value: "if value"),
|
||||
.text(value: "true"),
|
||||
.block(value: "else"),
|
||||
.text(value: "false"),
|
||||
.block(value: "endif")
|
||||
]
|
||||
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
@@ -29,12 +29,12 @@ func testIfNode() {
|
||||
}
|
||||
|
||||
$0.it("can parse an ifnot block") {
|
||||
let tokens = [
|
||||
Token.Block(value: "ifnot value"),
|
||||
Token.Text(value: "false"),
|
||||
Token.Block(value: "else"),
|
||||
Token.Text(value: "true"),
|
||||
Token.Block(value: "endif")
|
||||
let tokens: [Token] = [
|
||||
.block(value: "ifnot value"),
|
||||
.text(value: "false"),
|
||||
.block(value: "else"),
|
||||
.text(value: "true"),
|
||||
.block(value: "endif")
|
||||
]
|
||||
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
@@ -52,8 +52,8 @@ func testIfNode() {
|
||||
}
|
||||
|
||||
$0.it("throws an error when parsing an if block without an endif") {
|
||||
let tokens = [
|
||||
Token.Block(value: "if value"),
|
||||
let tokens: [Token] = [
|
||||
.block(value: "if value"),
|
||||
]
|
||||
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
@@ -62,8 +62,8 @@ func testIfNode() {
|
||||
}
|
||||
|
||||
$0.it("throws an error when parsing an ifnot without an endif") {
|
||||
let tokens = [
|
||||
Token.Block(value: "ifnot value"),
|
||||
let tokens: [Token] = [
|
||||
.block(value: "ifnot value"),
|
||||
]
|
||||
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
@@ -5,12 +5,12 @@ import PathKit
|
||||
|
||||
func testInclude() {
|
||||
describe("Include") {
|
||||
let path = Path(__FILE__) + ".." + "fixtures"
|
||||
let path = Path(#file) + ".." + "fixtures"
|
||||
let loader = TemplateLoader(paths: [path])
|
||||
|
||||
$0.describe("parsing") {
|
||||
$0.it("throws an error when no template is given") {
|
||||
let tokens = [ Token.Block(value: "include") ]
|
||||
let tokens: [Token] = [ .block(value: "include") ]
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
|
||||
let error = TemplateSyntaxError("'include' tag takes one argument, the template file to be included")
|
||||
@@ -18,7 +18,7 @@ func testInclude() {
|
||||
}
|
||||
|
||||
$0.it("can parse a valid include block") {
|
||||
let tokens = [ Token.Block(value: "include \"test.html\"") ]
|
||||
let tokens: [Token] = [ .block(value: "include \"test.html\"") ]
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
|
||||
let nodes = try parser.parse()
|
||||
@@ -33,7 +33,7 @@ func testInclude() {
|
||||
let node = IncludeNode(templateName: Variable("\"test.html\""))
|
||||
|
||||
do {
|
||||
try node.render(Context())
|
||||
_ = try node.render(Context())
|
||||
} catch {
|
||||
try expect("\(error)") == "Template loader not in context"
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func testInclude() {
|
||||
let node = IncludeNode(templateName: Variable("\"unknown.html\""))
|
||||
|
||||
do {
|
||||
try node.render(Context(dictionary: ["loader": loader]))
|
||||
_ = try node.render(Context(dictionary: ["loader": loader]))
|
||||
} catch {
|
||||
try expect("\(error)".hasPrefix("'unknown.html' template not found")).to.beTrue()
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import PathKit
|
||||
|
||||
func testInheritence() {
|
||||
describe("Inheritence") {
|
||||
let path = Path(__FILE__) + ".." + "fixtures"
|
||||
let path = Path(#file) + ".." + "fixtures"
|
||||
let loader = TemplateLoader(paths: [path])
|
||||
|
||||
$0.it("can inherit from another template") {
|
||||
@@ -9,7 +9,7 @@ func testLexer() {
|
||||
let tokens = lexer.tokenize()
|
||||
|
||||
try expect(tokens.count) == 1
|
||||
try expect(tokens.first) == Token.Text(value: "Hello World")
|
||||
try expect(tokens.first) == .text(value: "Hello World")
|
||||
}
|
||||
|
||||
$0.it("can tokenize a comment") {
|
||||
@@ -17,7 +17,7 @@ func testLexer() {
|
||||
let tokens = lexer.tokenize()
|
||||
|
||||
try expect(tokens.count) == (1)
|
||||
try expect(tokens.first) == Token.Comment(value: "Comment")
|
||||
try expect(tokens.first) == .comment(value: "Comment")
|
||||
}
|
||||
|
||||
$0.it("can tokenize a variable") {
|
||||
@@ -25,7 +25,7 @@ func testLexer() {
|
||||
let tokens = lexer.tokenize()
|
||||
|
||||
try expect(tokens.count) == 1
|
||||
try expect(tokens.first) == Token.Variable(value: "Variable")
|
||||
try expect(tokens.first) == .variable(value: "Variable")
|
||||
}
|
||||
|
||||
$0.it("can tokenize a mixture of content") {
|
||||
@@ -33,9 +33,9 @@ func testLexer() {
|
||||
let tokens = lexer.tokenize()
|
||||
|
||||
try expect(tokens.count) == 3
|
||||
try expect(tokens[0]) == Token.Text(value: "My name is ")
|
||||
try expect(tokens[1]) == Token.Variable(value: "name")
|
||||
try expect(tokens[2]) == Token.Text(value: ".")
|
||||
try expect(tokens[0]) == Token.text(value: "My name is ")
|
||||
try expect(tokens[1]) == Token.variable(value: "name")
|
||||
try expect(tokens[2]) == Token.text(value: ".")
|
||||
}
|
||||
|
||||
$0.it("can tokenize two variables without being greedy") {
|
||||
@@ -43,8 +43,8 @@ func testLexer() {
|
||||
let tokens = lexer.tokenize()
|
||||
|
||||
try expect(tokens.count) == 2
|
||||
try expect(tokens[0]) == Token.Variable(value: "thing")
|
||||
try expect(tokens[1]) == Token.Variable(value: "name")
|
||||
try expect(tokens[0]) == Token.variable(value: "thing")
|
||||
try expect(tokens[1]) == Token.variable(value: "name")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import Stencil
|
||||
|
||||
|
||||
class ErrorNode : NodeType {
|
||||
func render(context: Context) throws -> String {
|
||||
func render(_ context: Context) throws -> String {
|
||||
throw TemplateSyntaxError("Custom Error")
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ func testNowNode() {
|
||||
describe("NowNode") {
|
||||
$0.describe("parsing") {
|
||||
$0.it("parses default format without any now arguments") {
|
||||
let tokens = [ Token.Block(value: "now") ]
|
||||
let tokens: [Token] = [ .block(value: "now") ]
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
|
||||
let nodes = try parser.parse()
|
||||
@@ -18,7 +18,7 @@ func testNowNode() {
|
||||
}
|
||||
|
||||
$0.it("parses now with a format") {
|
||||
let tokens = [ Token.Block(value: "now \"HH:mm\"") ]
|
||||
let tokens: [Token] = [ .block(value: "now \"HH:mm\"") ]
|
||||
let parser = TokenParser(tokens: tokens, namespace: Namespace())
|
||||
let nodes = try parser.parse()
|
||||
let node = nodes.first as? NowNode
|
||||
@@ -31,9 +31,9 @@ func testNowNode() {
|
||||
$0.it("renders the date") {
|
||||
let node = NowNode(format: Variable("\"yyyy-MM-dd\""))
|
||||
|
||||
let formatter = NSDateFormatter()
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
let date = formatter.stringFromDate(NSDate())
|
||||
let date = formatter.string(from: NSDate() as Date)
|
||||
|
||||
try expect(try node.render(Context())) == date
|
||||
}
|
||||
@@ -6,7 +6,7 @@ func testTokenParser() {
|
||||
describe("TokenParser") {
|
||||
$0.it("can parse a text token") {
|
||||
let parser = TokenParser(tokens: [
|
||||
Token.Text(value: "Hello World")
|
||||
.text(value: "Hello World")
|
||||
], namespace: Namespace())
|
||||
|
||||
let nodes = try parser.parse()
|
||||
@@ -18,7 +18,7 @@ func testTokenParser() {
|
||||
|
||||
$0.it("can parse a variable token") {
|
||||
let parser = TokenParser(tokens: [
|
||||
Token.Variable(value: "'name'")
|
||||
.variable(value: "'name'")
|
||||
], namespace: Namespace())
|
||||
|
||||
let nodes = try parser.parse()
|
||||
@@ -30,7 +30,7 @@ func testTokenParser() {
|
||||
|
||||
$0.it("can parse a comment token") {
|
||||
let parser = TokenParser(tokens: [
|
||||
Token.Comment(value: "Secret stuff!")
|
||||
.comment(value: "Secret stuff!")
|
||||
], namespace: Namespace())
|
||||
|
||||
let nodes = try parser.parse()
|
||||
@@ -44,7 +44,7 @@ func testTokenParser() {
|
||||
}
|
||||
|
||||
let parser = TokenParser(tokens: [
|
||||
Token.Block(value: "known"),
|
||||
.block(value: "known"),
|
||||
], namespace: namespace)
|
||||
|
||||
let nodes = try parser.parse()
|
||||
@@ -53,7 +53,7 @@ func testTokenParser() {
|
||||
|
||||
$0.it("errors when parsing an unknown tag") {
|
||||
let parser = TokenParser(tokens: [
|
||||
Token.Block(value: "unknown"),
|
||||
.block(value: "unknown"),
|
||||
], namespace: Namespace())
|
||||
|
||||
try expect(try parser.parse()).toThrow(TemplateSyntaxError("Unknown template tag 'unknown'"))
|
||||
@@ -3,7 +3,7 @@ import Stencil
|
||||
|
||||
|
||||
class CustomNode : NodeType {
|
||||
func render(context:Context) throws -> String {
|
||||
func render(_ context:Context) throws -> String {
|
||||
return "Hello World"
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import PathKit
|
||||
|
||||
func testTemplateLoader() {
|
||||
describe("TemplateLoader") {
|
||||
let path = Path(__FILE__) + ".." + "fixtures"
|
||||
let path = Path(#file) + ".." + "fixtures"
|
||||
let loader = TemplateLoader(paths: [path])
|
||||
|
||||
$0.it("returns nil when a template cannot be found") {
|
||||
@@ -5,7 +5,7 @@ import Stencil
|
||||
func testToken() {
|
||||
describe("Token") {
|
||||
$0.it("can split the contents into components") {
|
||||
let token = Token.Text(value: "hello world")
|
||||
let token = Token.text(value: "hello world")
|
||||
let components = token.components()
|
||||
|
||||
try expect(components.count) == 2
|
||||
@@ -14,7 +14,7 @@ func testToken() {
|
||||
}
|
||||
|
||||
$0.it("can split the contents into components with single quoted strings") {
|
||||
let token = Token.Text(value: "hello 'kyle fuller'")
|
||||
let token = Token.text(value: "hello 'kyle fuller'")
|
||||
let components = token.components()
|
||||
|
||||
try expect(components.count) == 2
|
||||
@@ -23,7 +23,7 @@ func testToken() {
|
||||
}
|
||||
|
||||
$0.it("can split the contents into components with double quoted strings") {
|
||||
let token = Token.Text(value: "hello \"kyle fuller\"")
|
||||
let token = Token.text(value: "hello \"kyle fuller\"")
|
||||
let components = token.components()
|
||||
|
||||
try expect(components.count) == 2
|
||||
28
Tests/StencilTests/XCTest.swift
Normal file
28
Tests/StencilTests/XCTest.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
import XCTest
|
||||
|
||||
|
||||
public func stencilTests() {
|
||||
testContext()
|
||||
testFilter()
|
||||
testLexer()
|
||||
testToken()
|
||||
testTokenParser()
|
||||
testTemplateLoader()
|
||||
testTemplate()
|
||||
testVariable()
|
||||
testNode()
|
||||
testForNode()
|
||||
testIfNode()
|
||||
testNowNode()
|
||||
testInclude()
|
||||
testInheritence()
|
||||
testStencil()
|
||||
|
||||
}
|
||||
|
||||
|
||||
class StencilTests: XCTestCase {
|
||||
func testRunStencilTests() {
|
||||
stencilTests()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
testContext()
|
||||
testFilter()
|
||||
testLexer()
|
||||
testToken()
|
||||
testTokenParser()
|
||||
testTemplateLoader()
|
||||
testTemplate()
|
||||
testVariable()
|
||||
testNode()
|
||||
testForNode()
|
||||
testIfNode()
|
||||
testNowNode()
|
||||
testInclude()
|
||||
testInheritence()
|
||||
testStencil()
|
||||
Reference in New Issue
Block a user