[Project] Use 2 spaces for indentation
This commit is contained in:
@@ -123,7 +123,9 @@
|
|||||||
F0837D60120FB15CC00B9EBD /* Pods */,
|
F0837D60120FB15CC00B9EBD /* Pods */,
|
||||||
BE2E2DF8F488C4669126E920 /* Frameworks */,
|
BE2E2DF8F488C4669126E920 /* Frameworks */,
|
||||||
);
|
);
|
||||||
|
indentWidth = 2;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
tabWidth = 2;
|
||||||
};
|
};
|
||||||
77FAAE5319F91E480029DC5E /* Products */ = {
|
77FAAE5319F91E480029DC5E /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
|
|||||||
@@ -2,51 +2,51 @@ import Foundation
|
|||||||
|
|
||||||
/// A container for template variables.
|
/// A container for template variables.
|
||||||
public class Context : Equatable {
|
public class Context : Equatable {
|
||||||
var dictionaries:[Dictionary<String, AnyObject>]
|
var dictionaries:[Dictionary<String, AnyObject>]
|
||||||
|
|
||||||
public init(dictionary:Dictionary<String, AnyObject>) {
|
public init(dictionary:Dictionary<String, AnyObject>) {
|
||||||
dictionaries = [dictionary]
|
dictionaries = [dictionary]
|
||||||
}
|
}
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
dictionaries = []
|
dictionaries = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript(key: String) -> AnyObject? {
|
public subscript(key: String) -> AnyObject? {
|
||||||
/// Retrieves a variable's value, starting at the current context and going upwards
|
/// Retrieves a variable's value, starting at the current context and going upwards
|
||||||
get {
|
get {
|
||||||
for dictionary in reverse(dictionaries) {
|
for dictionary in reverse(dictionaries) {
|
||||||
if let value:AnyObject = dictionary[key] {
|
if let value:AnyObject = dictionary[key] {
|
||||||
return value
|
return value
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a variable in the current context, deleting the variable if it's nil
|
return nil
|
||||||
set(value) {
|
|
||||||
if dictionaries.count > 0 {
|
|
||||||
var dictionary = dictionaries.removeLast()
|
|
||||||
dictionary[key] = value
|
|
||||||
dictionaries.append(dictionary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func push() {
|
/// Set a variable in the current context, deleting the variable if it's nil
|
||||||
push(Dictionary<String, String>())
|
set(value) {
|
||||||
}
|
if dictionaries.count > 0 {
|
||||||
|
var dictionary = dictionaries.removeLast()
|
||||||
public func push(dictionary:Dictionary<String, String>) {
|
dictionary[key] = value
|
||||||
dictionaries.append(dictionary)
|
dictionaries.append(dictionary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func pop() {
|
public func push() {
|
||||||
dictionaries.removeLast()
|
push(Dictionary<String, String>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func push(dictionary:Dictionary<String, String>) {
|
||||||
|
dictionaries.append(dictionary)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func pop() {
|
||||||
|
dictionaries.removeLast()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ==(lhs:Context, rhs:Context) -> Bool {
|
public func ==(lhs:Context, rhs:Context) -> Bool {
|
||||||
return lhs.dictionaries == rhs.dictionaries
|
return lhs.dictionaries == rhs.dictionaries
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,57 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Lexer {
|
public struct Lexer {
|
||||||
public let templateString:String
|
public let templateString:String
|
||||||
let regex = NSRegularExpression(pattern: "(\\{\\{.*?\\}\\}|\\{%.*?%\\}|\\{#.*?#\\})", options: nil, error: nil)!
|
let regex = NSRegularExpression(pattern: "(\\{\\{.*?\\}\\}|\\{%.*?%\\}|\\{#.*?#\\})", options: nil, error: nil)!
|
||||||
|
|
||||||
public init(templateString:String) {
|
public init(templateString:String) {
|
||||||
self.templateString = templateString
|
self.templateString = templateString
|
||||||
|
}
|
||||||
|
|
||||||
|
func createToken(string:String) -> Token {
|
||||||
|
func strip() -> String {
|
||||||
|
return string[string.startIndex.successor().successor()..<string.endIndex.predecessor().predecessor()].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
func createToken(string:String) -> Token {
|
if string.hasPrefix("{{") {
|
||||||
func strip() -> String {
|
return Token.Variable(value: strip())
|
||||||
return string[string.startIndex.successor().successor()..<string.endIndex.predecessor().predecessor()].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
|
} else if string.hasPrefix("{%") {
|
||||||
}
|
return Token.Block(value: strip())
|
||||||
|
} else if string.hasPrefix("{#") {
|
||||||
if string.hasPrefix("{{") {
|
return Token.Comment(value: strip())
|
||||||
return Token.Variable(value: strip())
|
|
||||||
} else if string.hasPrefix("{%") {
|
|
||||||
return Token.Block(value: strip())
|
|
||||||
} else if string.hasPrefix("{#") {
|
|
||||||
return Token.Comment(value: strip())
|
|
||||||
}
|
|
||||||
|
|
||||||
return Token.Text(value: string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an array of tokens from a given template string.
|
return Token.Text(value: string)
|
||||||
public func tokenize() -> [Token] {
|
}
|
||||||
// Unfortunately NSRegularExpression doesn't have a split.
|
|
||||||
// So here's a really terrible implementation
|
|
||||||
|
|
||||||
var tokens = [Token]()
|
/// Returns an array of tokens from a given template string.
|
||||||
|
public func tokenize() -> [Token] {
|
||||||
|
// Unfortunately NSRegularExpression doesn't have a split.
|
||||||
|
// So here's a really terrible implementation
|
||||||
|
|
||||||
let range = NSMakeRange(0, count(templateString))
|
var tokens = [Token]()
|
||||||
var lastIndex = 0
|
|
||||||
let nsTemplateString = templateString as NSString
|
|
||||||
let options = NSMatchingOptions(0)
|
|
||||||
regex.enumerateMatchesInString(templateString, options: options, range: range) { (result, flags, b) in
|
|
||||||
if result.range.location != lastIndex {
|
|
||||||
let previousMatch = nsTemplateString.substringWithRange(NSMakeRange(lastIndex, result.range.location - lastIndex))
|
|
||||||
tokens.append(self.createToken(previousMatch))
|
|
||||||
}
|
|
||||||
|
|
||||||
let match = nsTemplateString.substringWithRange(result.range)
|
let range = NSMakeRange(0, count(templateString))
|
||||||
tokens.append(self.createToken(match))
|
var lastIndex = 0
|
||||||
|
let nsTemplateString = templateString as NSString
|
||||||
|
let options = NSMatchingOptions(0)
|
||||||
|
regex.enumerateMatchesInString(templateString, options: options, range: range) { (result, flags, b) in
|
||||||
|
if result.range.location != lastIndex {
|
||||||
|
let previousMatch = nsTemplateString.substringWithRange(NSMakeRange(lastIndex, result.range.location - lastIndex))
|
||||||
|
tokens.append(self.createToken(previousMatch))
|
||||||
|
}
|
||||||
|
|
||||||
lastIndex = result.range.location + result.range.length
|
let match = nsTemplateString.substringWithRange(result.range)
|
||||||
}
|
tokens.append(self.createToken(match))
|
||||||
|
|
||||||
if lastIndex < count(templateString) {
|
lastIndex = result.range.location + result.range.length
|
||||||
let substring = (templateString as NSString).substringFromIndex(lastIndex)
|
|
||||||
tokens.append(Token.Text(value: substring))
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if lastIndex < count(templateString) {
|
||||||
|
let substring = (templateString as NSString).substringFromIndex(lastIndex)
|
||||||
|
tokens.append(Token.Text(value: substring))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,304 +1,304 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct NodeError : Error {
|
struct NodeError : Error {
|
||||||
let token:Token
|
let token:Token
|
||||||
let message:String
|
let message:String
|
||||||
|
|
||||||
init(token:Token, message:String) {
|
init(token:Token, message:String) {
|
||||||
self.token = token
|
self.token = token
|
||||||
self.message = message
|
self.message = message
|
||||||
}
|
}
|
||||||
|
|
||||||
var description:String {
|
var description:String {
|
||||||
return "\(token.components().first!): \(message)"
|
return "\(token.components().first!): \(message)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol Node {
|
public protocol Node {
|
||||||
/// Return the node rendered as a string, or returns a failure
|
/// Return the node rendered as a string, or returns a failure
|
||||||
func render(context:Context) -> Result
|
func render(context:Context) -> Result
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Array {
|
extension Array {
|
||||||
func map<U>(block:((Element) -> (U?, Error?))) -> ([U]?, Error?) {
|
func map<U>(block:((Element) -> (U?, Error?))) -> ([U]?, Error?) {
|
||||||
var results = [U]()
|
var results = [U]()
|
||||||
|
|
||||||
for item in self {
|
for item in self {
|
||||||
let (result, error) = block(item)
|
let (result, error) = block(item)
|
||||||
|
|
||||||
if let error = error {
|
if let error = error {
|
||||||
return (nil, error)
|
return (nil, error)
|
||||||
} else if (result != nil) {
|
} else if (result != nil) {
|
||||||
// let result = result exposing a bug in the Swift compier :(
|
// let result = result exposing a bug in the Swift compier :(
|
||||||
results.append(result!)
|
results.append(result!)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return (results, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (results, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func renderNodes(nodes:[Node], context:Context) -> Result {
|
public func renderNodes(nodes:[Node], context:Context) -> Result {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
for item in nodes {
|
for item in nodes {
|
||||||
switch item.render(context) {
|
switch item.render(context) {
|
||||||
case .Success(let string):
|
case .Success(let string):
|
||||||
result += string
|
result += string
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
return .Error(error)
|
return .Error(error)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return .Success(result)
|
return .Success(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SimpleNode : Node {
|
public class SimpleNode : Node {
|
||||||
let handler:(Context) -> (Result)
|
let handler:(Context) -> (Result)
|
||||||
|
|
||||||
public init(handler:((Context) -> (Result))) {
|
public init(handler:((Context) -> (Result))) {
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
public func render(context:Context) -> Result {
|
public func render(context:Context) -> Result {
|
||||||
return handler(context)
|
return handler(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TextNode : Node {
|
public class TextNode : Node {
|
||||||
public let text:String
|
public let text:String
|
||||||
|
|
||||||
public init(text:String) {
|
public init(text:String) {
|
||||||
self.text = text
|
self.text = text
|
||||||
}
|
}
|
||||||
|
|
||||||
public func render(context:Context) -> Result {
|
public func render(context:Context) -> Result {
|
||||||
return .Success(self.text)
|
return .Success(self.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VariableNode : Node {
|
public class VariableNode : Node {
|
||||||
public let variable:Variable
|
public let variable:Variable
|
||||||
|
|
||||||
public init(variable:Variable) {
|
public init(variable:Variable) {
|
||||||
self.variable = variable
|
self.variable = variable
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(variable:String) {
|
||||||
|
self.variable = Variable(variable)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func render(context:Context) -> Result {
|
||||||
|
let result:AnyObject? = variable.resolve(context)
|
||||||
|
|
||||||
|
if let result = result as? String {
|
||||||
|
return .Success(result)
|
||||||
|
} else if let result = result as? NSObject {
|
||||||
|
return .Success(result.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(variable:String) {
|
return .Success("")
|
||||||
self.variable = Variable(variable)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public func render(context:Context) -> Result {
|
|
||||||
let result:AnyObject? = variable.resolve(context)
|
|
||||||
|
|
||||||
if let result = result as? String {
|
|
||||||
return .Success(result)
|
|
||||||
} else if let result = result as? NSObject {
|
|
||||||
return .Success(result.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Success("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NowNode : Node {
|
public class NowNode : Node {
|
||||||
public let format:Variable
|
public let format:Variable
|
||||||
|
|
||||||
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
||||||
var format:Variable?
|
var format:Variable?
|
||||||
|
|
||||||
let components = token.components()
|
let components = token.components()
|
||||||
if components.count == 2 {
|
if components.count == 2 {
|
||||||
format = Variable(components[1])
|
format = Variable(components[1])
|
||||||
}
|
|
||||||
|
|
||||||
return .Success(node:NowNode(format:format))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(format:Variable?) {
|
return .Success(node:NowNode(format:format))
|
||||||
if let format = format {
|
}
|
||||||
self.format = format
|
|
||||||
} else {
|
public init(format:Variable?) {
|
||||||
self.format = Variable("\"yyyy-MM-dd 'at' HH:mm\"")
|
if let format = format {
|
||||||
}
|
self.format = format
|
||||||
|
} else {
|
||||||
|
self.format = Variable("\"yyyy-MM-dd 'at' HH:mm\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func render(context: Context) -> Result {
|
||||||
|
let date = NSDate()
|
||||||
|
let format: AnyObject? = self.format.resolve(context)
|
||||||
|
var formatter:NSDateFormatter?
|
||||||
|
|
||||||
|
if let format = format as? NSDateFormatter {
|
||||||
|
formatter = format
|
||||||
|
} else if let format = format as? String {
|
||||||
|
formatter = NSDateFormatter()
|
||||||
|
formatter!.dateFormat = format
|
||||||
|
} else {
|
||||||
|
return .Success("")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func render(context: Context) -> Result {
|
return .Success(formatter!.stringFromDate(date))
|
||||||
let date = NSDate()
|
}
|
||||||
let format: AnyObject? = self.format.resolve(context)
|
|
||||||
var formatter:NSDateFormatter?
|
|
||||||
|
|
||||||
if let format = format as? NSDateFormatter {
|
|
||||||
formatter = format
|
|
||||||
} else if let format = format as? String {
|
|
||||||
formatter = NSDateFormatter()
|
|
||||||
formatter!.dateFormat = format
|
|
||||||
} else {
|
|
||||||
return .Success("")
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Success(formatter!.stringFromDate(date))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ForNode : Node {
|
public class ForNode : Node {
|
||||||
let variable:Variable
|
let variable:Variable
|
||||||
let loopVariable:String
|
let loopVariable:String
|
||||||
let nodes:[Node]
|
let nodes:[Node]
|
||||||
|
|
||||||
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
||||||
let components = token.components()
|
let components = token.components()
|
||||||
|
|
||||||
if count(components) == 4 && components[2] == "in" {
|
if count(components) == 4 && components[2] == "in" {
|
||||||
let loopVariable = components[1]
|
let loopVariable = components[1]
|
||||||
let variable = components[3]
|
let variable = components[3]
|
||||||
|
|
||||||
var forNodes:[Node]!
|
var forNodes:[Node]!
|
||||||
var emptyNodes = [Node]()
|
var emptyNodes = [Node]()
|
||||||
|
|
||||||
switch parser.parse(until(["endfor", "empty"])) {
|
switch parser.parse(until(["endfor", "empty"])) {
|
||||||
case .Success(let nodes):
|
case .Success(let nodes):
|
||||||
forNodes = nodes
|
forNodes = nodes
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
return .Error(error: error)
|
return .Error(error: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let token = parser.nextToken() {
|
if let token = parser.nextToken() {
|
||||||
if token.contents == "empty" {
|
if token.contents == "empty" {
|
||||||
switch parser.parse(until(["endfor"])) {
|
switch parser.parse(until(["endfor"])) {
|
||||||
case .Success(let nodes):
|
case .Success(let nodes):
|
||||||
emptyNodes = nodes
|
emptyNodes = nodes
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
return .Error(error: error)
|
return .Error(error: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.nextToken()
|
parser.nextToken()
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .Error(error: NodeError(token: token, message: "`endfor` was not found."))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Success(node:ForNode(variable: variable, loopVariable: loopVariable, nodes: forNodes, emptyNodes:emptyNodes))
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return .Error(error: NodeError(token: token, message: "`endfor` was not found."))
|
||||||
|
}
|
||||||
|
|
||||||
return .Error(error: NodeError(token: token, message: "Invalid syntax. Expected `for x in y`."))
|
return .Success(node:ForNode(variable: variable, loopVariable: loopVariable, nodes: forNodes, emptyNodes:emptyNodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(variable:String, loopVariable:String, nodes:[Node], emptyNodes:[Node]) {
|
return .Error(error: NodeError(token: token, message: "Invalid syntax. Expected `for x in y`."))
|
||||||
self.variable = Variable(variable)
|
}
|
||||||
self.loopVariable = loopVariable
|
|
||||||
self.nodes = nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
public func render(context: Context) -> Result {
|
public init(variable:String, loopVariable:String, nodes:[Node], emptyNodes:[Node]) {
|
||||||
let values = variable.resolve(context) as? [AnyObject]
|
self.variable = Variable(variable)
|
||||||
var output = ""
|
self.loopVariable = loopVariable
|
||||||
|
self.nodes = nodes
|
||||||
|
}
|
||||||
|
|
||||||
if let values = values {
|
public func render(context: Context) -> Result {
|
||||||
for item in values {
|
let values = variable.resolve(context) as? [AnyObject]
|
||||||
context.push()
|
var output = ""
|
||||||
context[loopVariable] = item
|
|
||||||
let result = renderNodes(nodes, context)
|
|
||||||
context.pop()
|
|
||||||
|
|
||||||
switch result {
|
if let values = values {
|
||||||
case .Success(let string):
|
for item in values {
|
||||||
output += string
|
context.push()
|
||||||
case .Error(let error):
|
context[loopVariable] = item
|
||||||
return .Error(error)
|
let result = renderNodes(nodes, context)
|
||||||
}
|
context.pop()
|
||||||
}
|
|
||||||
|
switch result {
|
||||||
|
case .Success(let string):
|
||||||
|
output += string
|
||||||
|
case .Error(let error):
|
||||||
|
return .Error(error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return .Success(output)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return .Success(output)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IfNode : Node {
|
public class IfNode : Node {
|
||||||
public let variable:Variable
|
public let variable:Variable
|
||||||
public let trueNodes:[Node]
|
public let trueNodes:[Node]
|
||||||
public let falseNodes:[Node]
|
public let falseNodes:[Node]
|
||||||
|
|
||||||
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
||||||
let variable = token.components()[1]
|
let variable = token.components()[1]
|
||||||
var trueNodes = [Node]()
|
var trueNodes = [Node]()
|
||||||
var falseNodes = [Node]()
|
var falseNodes = [Node]()
|
||||||
|
|
||||||
switch parser.parse(until(["endif", "else"])) {
|
switch parser.parse(until(["endif", "else"])) {
|
||||||
case .Success(let nodes):
|
case .Success(let nodes):
|
||||||
trueNodes = nodes
|
trueNodes = nodes
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
return .Error(error: error)
|
return .Error(error: error)
|
||||||
}
|
|
||||||
|
|
||||||
if let token = parser.nextToken() {
|
|
||||||
if token.contents == "else" {
|
|
||||||
switch parser.parse(until(["endif"])) {
|
|
||||||
case .Success(let nodes):
|
|
||||||
falseNodes = nodes
|
|
||||||
case .Error(let error):
|
|
||||||
return .Error(error: error)
|
|
||||||
}
|
|
||||||
parser.nextToken()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .Error(error:NodeError(token: token, message: "`endif` was not found."))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result {
|
if let token = parser.nextToken() {
|
||||||
let variable = token.components()[1]
|
if token.contents == "else" {
|
||||||
var trueNodes = [Node]()
|
switch parser.parse(until(["endif"])) {
|
||||||
var falseNodes = [Node]()
|
|
||||||
|
|
||||||
switch parser.parse(until(["endif", "else"])) {
|
|
||||||
case .Success(let nodes):
|
case .Success(let nodes):
|
||||||
falseNodes = nodes
|
falseNodes = nodes
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
return .Error(error: error)
|
return .Error(error: error)
|
||||||
}
|
}
|
||||||
|
parser.nextToken()
|
||||||
if let token = parser.nextToken() {
|
}
|
||||||
if token.contents == "else" {
|
} else {
|
||||||
switch parser.parse(until(["endif"])) {
|
return .Error(error:NodeError(token: token, message: "`endif` was not found."))
|
||||||
case .Success(let nodes):
|
|
||||||
trueNodes = nodes
|
|
||||||
case .Error(let error):
|
|
||||||
return .Error(error: error)
|
|
||||||
}
|
|
||||||
parser.nextToken()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .Error(error:NodeError(token: token, message: "`endif` was not found."))
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(variable:String, trueNodes:[Node], falseNodes:[Node]) {
|
return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes))
|
||||||
self.variable = Variable(variable)
|
}
|
||||||
self.trueNodes = trueNodes
|
|
||||||
self.falseNodes = falseNodes
|
public class func parse_ifnot(parser:TokenParser, token:Token) -> TokenParser.Result {
|
||||||
|
let variable = token.components()[1]
|
||||||
|
var trueNodes = [Node]()
|
||||||
|
var falseNodes = [Node]()
|
||||||
|
|
||||||
|
switch parser.parse(until(["endif", "else"])) {
|
||||||
|
case .Success(let nodes):
|
||||||
|
falseNodes = nodes
|
||||||
|
case .Error(let error):
|
||||||
|
return .Error(error: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func render(context: Context) -> Result {
|
if let token = parser.nextToken() {
|
||||||
let result: AnyObject? = variable.resolve(context)
|
if token.contents == "else" {
|
||||||
var truthy = false
|
switch parser.parse(until(["endif"])) {
|
||||||
|
case .Success(let nodes):
|
||||||
if let result = result as? [AnyObject] {
|
trueNodes = nodes
|
||||||
if result.count > 0 {
|
case .Error(let error):
|
||||||
truthy = true
|
return .Error(error: error)
|
||||||
}
|
|
||||||
} else if let result: AnyObject = result {
|
|
||||||
truthy = true
|
|
||||||
}
|
}
|
||||||
|
parser.nextToken()
|
||||||
context.push()
|
}
|
||||||
let output = renderNodes(truthy ? trueNodes : falseNodes, context)
|
} else {
|
||||||
context.pop()
|
return .Error(error:NodeError(token: token, message: "`endif` was not found."))
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return .Success(node:IfNode(variable: variable, trueNodes: trueNodes, falseNodes: falseNodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(variable:String, trueNodes:[Node], falseNodes:[Node]) {
|
||||||
|
self.variable = Variable(variable)
|
||||||
|
self.trueNodes = trueNodes
|
||||||
|
self.falseNodes = falseNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
public func render(context: Context) -> Result {
|
||||||
|
let result: AnyObject? = variable.resolve(context)
|
||||||
|
var truthy = false
|
||||||
|
|
||||||
|
if let result = result as? [AnyObject] {
|
||||||
|
if result.count > 0 {
|
||||||
|
truthy = true
|
||||||
|
}
|
||||||
|
} else if let result: AnyObject = result {
|
||||||
|
truthy = true
|
||||||
|
}
|
||||||
|
|
||||||
|
context.push()
|
||||||
|
let output = renderNodes(truthy ? trueNodes : falseNodes, context)
|
||||||
|
context.pop()
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,109 +1,109 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public func until(tags:[String])(parser:TokenParser, token:Token) -> Bool {
|
public func until(tags:[String])(parser:TokenParser, token:Token) -> Bool {
|
||||||
if let name = token.components().first {
|
if let name = token.components().first {
|
||||||
for tag in tags {
|
for tag in tags {
|
||||||
if name == tag {
|
if name == tag {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A class for parsing an array of tokens and converts them into a collection of Node's
|
/// A class for parsing an array of tokens and converts them into a collection of Node's
|
||||||
public class TokenParser {
|
public class TokenParser {
|
||||||
public typealias TagParser = (TokenParser, Token) -> Result
|
public typealias TagParser = (TokenParser, Token) -> Result
|
||||||
public typealias NodeList = [Node]
|
public typealias NodeList = [Node]
|
||||||
|
|
||||||
public enum Result {
|
public enum Result {
|
||||||
case Success(node: Node)
|
case Success(node: Node)
|
||||||
case Error(error: Stencil.Error)
|
case Error(error: Stencil.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Results {
|
public enum Results {
|
||||||
case Success(nodes: NodeList)
|
case Success(nodes: NodeList)
|
||||||
case Error(error: Stencil.Error)
|
case Error(error: Stencil.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var tokens:[Token]
|
private var tokens:[Token]
|
||||||
private var tags = Dictionary<String, TagParser>()
|
private var tags = Dictionary<String, TagParser>()
|
||||||
|
|
||||||
public init(tokens:[Token]) {
|
public init(tokens:[Token]) {
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
registerTag("for", parser: ForNode.parse)
|
registerTag("for", parser: ForNode.parse)
|
||||||
registerTag("if", parser: IfNode.parse)
|
registerTag("if", parser: IfNode.parse)
|
||||||
registerTag("ifnot", parser: IfNode.parse_ifnot)
|
registerTag("ifnot", parser: IfNode.parse_ifnot)
|
||||||
registerTag("now", parser: NowNode.parse)
|
registerTag("now", parser: NowNode.parse)
|
||||||
registerTag("include", parser: IncludeNode.parse)
|
registerTag("include", parser: IncludeNode.parse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a new template tag
|
/// Registers a new template tag
|
||||||
public func registerTag(name:String, parser:TagParser) {
|
public func registerTag(name:String, parser:TagParser) {
|
||||||
tags[name] = parser
|
tags[name] = parser
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a simple template tag with a name and a handler
|
/// Registers a simple template tag with a name and a handler
|
||||||
public func registerSimpleTag(name:String, handler:((Context) -> (Stencil.Result))) {
|
public func registerSimpleTag(name:String, handler:((Context) -> (Stencil.Result))) {
|
||||||
registerTag(name, parser: { (parser, token) -> TokenParser.Result in
|
registerTag(name, parser: { (parser, token) -> TokenParser.Result in
|
||||||
return .Success(node:SimpleNode(handler: handler))
|
return .Success(node:SimpleNode(handler: handler))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the given tokens into nodes
|
/// Parse the given tokens into nodes
|
||||||
public func parse() -> Results {
|
public func parse() -> Results {
|
||||||
return parse(nil)
|
return parse(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> TokenParser.Results {
|
public func parse(parse_until:((parser:TokenParser, token:Token) -> (Bool))?) -> TokenParser.Results {
|
||||||
var nodes = NodeList()
|
var nodes = NodeList()
|
||||||
|
|
||||||
while tokens.count > 0 {
|
while tokens.count > 0 {
|
||||||
let token = nextToken()!
|
let token = nextToken()!
|
||||||
|
|
||||||
switch token {
|
switch token {
|
||||||
case .Text(let text):
|
case .Text(let text):
|
||||||
nodes.append(TextNode(text: text))
|
nodes.append(TextNode(text: text))
|
||||||
case .Variable(let variable):
|
case .Variable(let variable):
|
||||||
nodes.append(VariableNode(variable: variable))
|
nodes.append(VariableNode(variable: variable))
|
||||||
case .Block(let value):
|
case .Block(let value):
|
||||||
let tag = token.components().first
|
let tag = token.components().first
|
||||||
|
|
||||||
if let parse_until = parse_until {
|
if let parse_until = parse_until {
|
||||||
if parse_until(parser: self, token: token) {
|
if parse_until(parser: self, token: token) {
|
||||||
prependToken(token)
|
prependToken(token)
|
||||||
return .Success(nodes:nodes)
|
return .Success(nodes:nodes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let tag = tag {
|
if let tag = tag {
|
||||||
if let parser = self.tags[tag] {
|
if let parser = self.tags[tag] {
|
||||||
switch parser(self, token) {
|
switch parser(self, token) {
|
||||||
case .Success(let node):
|
case .Success(let node):
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
return .Error(error:error)
|
return .Error(error:error)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .Comment(let value):
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
case .Comment(let value):
|
||||||
return .Success(nodes:nodes)
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func nextToken() -> Token? {
|
return .Success(nodes:nodes)
|
||||||
if tokens.count > 0 {
|
}
|
||||||
return tokens.removeAtIndex(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
public func nextToken() -> Token? {
|
||||||
|
if tokens.count > 0 {
|
||||||
|
return tokens.removeAtIndex(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func prependToken(token:Token) {
|
return nil
|
||||||
tokens.insert(token, atIndex: 0)
|
}
|
||||||
}
|
|
||||||
|
public func prependToken(token:Token) {
|
||||||
|
tokens.insert(token, atIndex: 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol Error : Printable {
|
public protocol Error : Printable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ==(lhs:Error, rhs:Error) -> Bool {
|
public func ==(lhs:Error, rhs:Error) -> Bool {
|
||||||
return lhs.description == rhs.description
|
return lhs.description == rhs.description
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Result : Equatable {
|
public enum Result : Equatable {
|
||||||
case Success(String)
|
case Success(String)
|
||||||
case Error(Stencil.Error)
|
case Error(Stencil.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ==(lhs:Result, rhs:Result) -> Bool {
|
public func ==(lhs:Result, rhs:Result) -> Bool {
|
||||||
switch (lhs, rhs) {
|
switch (lhs, rhs) {
|
||||||
case (.Success(let lhsValue), .Success(let rhsValue)):
|
case (.Success(let lhsValue), .Success(let rhsValue)):
|
||||||
return lhsValue == rhsValue
|
return lhsValue == rhsValue
|
||||||
case (.Error(let lhsValue), .Error(let rhsValue)):
|
case (.Error(let lhsValue), .Error(let rhsValue)):
|
||||||
return lhsValue == rhsValue
|
return lhsValue == rhsValue
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,71 +3,71 @@ import PathKit
|
|||||||
|
|
||||||
/// A class representing a template
|
/// A class representing a template
|
||||||
public class Template {
|
public class Template {
|
||||||
public let parser:TokenParser
|
public let parser:TokenParser
|
||||||
|
|
||||||
/// Create a template with the given name inside the main bundle
|
/// Create a template with the given name inside the main bundle
|
||||||
public convenience init?(named:String) {
|
public convenience init?(named:String) {
|
||||||
self.init(named:named, inBundle:nil)
|
self.init(named:named, inBundle:nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a template with the given name inside the given bundle
|
||||||
|
public convenience init?(named:String, inBundle bundle:NSBundle?) {
|
||||||
|
var url:NSURL?
|
||||||
|
|
||||||
|
if let bundle = bundle {
|
||||||
|
url = bundle.URLForResource(named, withExtension: nil)
|
||||||
|
} else {
|
||||||
|
url = NSBundle.mainBundle().URLForResource(named, withExtension: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a template with the given name inside the given bundle
|
self.init(URL:url!)
|
||||||
public convenience init?(named:String, inBundle bundle:NSBundle?) {
|
}
|
||||||
var url:NSURL?
|
|
||||||
|
|
||||||
if let bundle = bundle {
|
/// Create a template with a file found at the given URL
|
||||||
url = bundle.URLForResource(named, withExtension: nil)
|
public convenience init?(URL:NSURL) {
|
||||||
} else {
|
var error:NSError?
|
||||||
url = NSBundle.mainBundle().URLForResource(named, withExtension: nil)
|
let maybeTemplateString = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: &error)
|
||||||
}
|
if let templateString = maybeTemplateString {
|
||||||
|
self.init(templateString:templateString as String)
|
||||||
self.init(URL:url!)
|
} else {
|
||||||
|
self.init(templateString:"")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a template with a file found at the given URL
|
/// Create a template with a file found at the given path
|
||||||
public convenience init?(URL:NSURL) {
|
public convenience init?(path:Path) {
|
||||||
var error:NSError?
|
var error:NSError?
|
||||||
let maybeTemplateString = NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding, error: &error)
|
|
||||||
if let templateString = maybeTemplateString {
|
if let string:String = path.read() {
|
||||||
self.init(templateString:templateString as String)
|
self.init(templateString:string)
|
||||||
} else {
|
} else {
|
||||||
self.init(templateString:"")
|
self.init(templateString:"")
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a template with a file found at the given path
|
/// Create a template with a template string
|
||||||
public convenience init?(path:Path) {
|
public init(templateString:String) {
|
||||||
var error:NSError?
|
let lexer = Lexer(templateString: templateString)
|
||||||
|
let tokens = lexer.tokenize()
|
||||||
|
parser = TokenParser(tokens: tokens)
|
||||||
|
}
|
||||||
|
|
||||||
if let string:String = path.read() {
|
/// Render the given template in a context
|
||||||
self.init(templateString:string)
|
public func render(context:Context) -> Result {
|
||||||
} else {
|
switch parser.parse() {
|
||||||
self.init(templateString:"")
|
case .Success(let nodes):
|
||||||
return nil
|
return renderNodes(nodes, context)
|
||||||
}
|
|
||||||
|
case .Error(let error):
|
||||||
|
return .Error(error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a template with a template string
|
/// Render the given template without a context
|
||||||
public init(templateString:String) {
|
public func render() -> Result {
|
||||||
let lexer = Lexer(templateString: templateString)
|
let context = Context()
|
||||||
let tokens = lexer.tokenize()
|
return render(context)
|
||||||
parser = TokenParser(tokens: tokens)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Render the given template in a context
|
|
||||||
public func render(context:Context) -> Result {
|
|
||||||
switch parser.parse() {
|
|
||||||
case .Success(let nodes):
|
|
||||||
return renderNodes(nodes, context)
|
|
||||||
|
|
||||||
case .Error(let error):
|
|
||||||
return .Error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render the given template without a context
|
|
||||||
public func render() -> Result {
|
|
||||||
let context = Context()
|
|
||||||
return render(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,35 +11,35 @@ import PathKit
|
|||||||
|
|
||||||
// A class for loading a template from disk
|
// A class for loading a template from disk
|
||||||
public class TemplateLoader {
|
public class TemplateLoader {
|
||||||
public let paths:[Path]
|
public let paths:[Path]
|
||||||
|
|
||||||
public init(paths:[Path]) {
|
public init(paths:[Path]) {
|
||||||
self.paths = paths
|
self.paths = paths
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(bundle:[NSBundle]) {
|
||||||
|
self.paths = bundle.map {
|
||||||
|
return Path($0.bundlePath)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public init(bundle:[NSBundle]) {
|
public func loadTemplate(templateName:String) -> Template? {
|
||||||
self.paths = bundle.map {
|
return loadTemplate([templateName])
|
||||||
return Path($0.bundlePath)
|
}
|
||||||
|
|
||||||
|
public func loadTemplate(templateNames:[String]) -> Template? {
|
||||||
|
for path in paths {
|
||||||
|
for templateName in templateNames {
|
||||||
|
let templatePath = path + Path(templateName)
|
||||||
|
|
||||||
|
if templatePath.exists() {
|
||||||
|
if let template = Template(path: templatePath) {
|
||||||
|
return template
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadTemplate(templateName:String) -> Template? {
|
return nil
|
||||||
return loadTemplate([templateName])
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public func loadTemplate(templateNames:[String]) -> Template? {
|
|
||||||
for path in paths {
|
|
||||||
for templateName in templateNames {
|
|
||||||
let templatePath = path + Path(templateName)
|
|
||||||
|
|
||||||
if templatePath.exists() {
|
|
||||||
if let template = Template(path: templatePath) {
|
|
||||||
return template
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,43 +2,43 @@ import Foundation
|
|||||||
import PathKit
|
import PathKit
|
||||||
|
|
||||||
extension String : Error {
|
extension String : Error {
|
||||||
public var description:String {
|
public var description:String {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IncludeNode : Node {
|
public class IncludeNode : Node {
|
||||||
public let templateName:String
|
public let templateName:String
|
||||||
|
|
||||||
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
public class func parse(parser:TokenParser, token:Token) -> TokenParser.Result {
|
||||||
let bits = token.contents.componentsSeparatedByString("\"")
|
let bits = token.contents.componentsSeparatedByString("\"")
|
||||||
|
|
||||||
if bits.count != 3 {
|
if bits.count != 3 {
|
||||||
return .Error(error:NodeError(token: token, message: "Tag takes one argument, the template file to be included"))
|
return .Error(error:NodeError(token: token, message: "Tag takes one argument, the template file to be included"))
|
||||||
}
|
|
||||||
|
|
||||||
return .Success(node:IncludeNode(templateName: bits[1]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(templateName:String) {
|
return .Success(node:IncludeNode(templateName: bits[1]))
|
||||||
self.templateName = templateName
|
}
|
||||||
|
|
||||||
|
public init(templateName:String) {
|
||||||
|
self.templateName = templateName
|
||||||
|
}
|
||||||
|
|
||||||
|
public func render(context: Context) -> Result {
|
||||||
|
if let loader = context["loader"] as? TemplateLoader {
|
||||||
|
if let template = loader.loadTemplate(templateName) {
|
||||||
|
return template.render(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths:String = join(", ", loader.paths.map { path in
|
||||||
|
return path.description
|
||||||
|
})
|
||||||
|
let error = "Template '\(templateName)' not found in \(paths)"
|
||||||
|
return .Error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func render(context: Context) -> Result {
|
let error = "Template loader not in context"
|
||||||
if let loader = context["loader"] as? TemplateLoader {
|
return .Error(error)
|
||||||
if let template = loader.loadTemplate(templateName) {
|
}
|
||||||
return template.render(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
let paths:String = join(", ", loader.paths.map { path in
|
|
||||||
return path.description
|
|
||||||
})
|
|
||||||
let error = "Template '\(templateName)' not found in \(paths)"
|
|
||||||
return .Error(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
let error = "Template loader not in context"
|
|
||||||
return .Error(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,64 +2,64 @@ import Foundation
|
|||||||
|
|
||||||
|
|
||||||
public enum Token : Equatable {
|
public enum Token : Equatable {
|
||||||
/// A token representing a piece of text.
|
/// A token representing a piece of text.
|
||||||
case Text(value:String)
|
case Text(value:String)
|
||||||
|
|
||||||
/// A token representing a variable.
|
/// A token representing a variable.
|
||||||
case Variable(value:String)
|
case Variable(value:String)
|
||||||
|
|
||||||
/// A token representing a comment.
|
/// A token representing a comment.
|
||||||
case Comment(value:String)
|
case Comment(value:String)
|
||||||
|
|
||||||
/// A token representing a template block.
|
/// A token representing a template block.
|
||||||
case Block(value:String)
|
case Block(value:String)
|
||||||
|
|
||||||
/// Returns the underlying value as an array seperated by spaces
|
/// Returns the underlying value as an array seperated by spaces
|
||||||
func components() -> [String] {
|
func components() -> [String] {
|
||||||
// TODO: Make this smarter and treat quoted strings as a single component
|
// TODO: Make this smarter and treat quoted strings as a single component
|
||||||
let characterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet()
|
let characterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet()
|
||||||
|
|
||||||
func strip(value: String) -> [String] {
|
|
||||||
return value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch self {
|
func strip(value: String) -> [String] {
|
||||||
case .Block(let value):
|
return value.stringByTrimmingCharactersInSet(characterSet).componentsSeparatedByCharactersInSet(characterSet)
|
||||||
return strip(value)
|
|
||||||
case .Variable(let value):
|
|
||||||
return strip(value)
|
|
||||||
case .Text(let value):
|
|
||||||
return strip(value)
|
|
||||||
case .Comment(let value):
|
|
||||||
return strip(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var contents:String {
|
switch self {
|
||||||
switch self {
|
case .Block(let value):
|
||||||
case .Block(let value):
|
return strip(value)
|
||||||
return value
|
case .Variable(let value):
|
||||||
case .Variable(let value):
|
return strip(value)
|
||||||
return value
|
case .Text(let value):
|
||||||
case .Text(let value):
|
return strip(value)
|
||||||
return value
|
case .Comment(let value):
|
||||||
case .Comment(let value):
|
return strip(value)
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var contents:String {
|
||||||
|
switch self {
|
||||||
|
case .Block(let value):
|
||||||
|
return value
|
||||||
|
case .Variable(let value):
|
||||||
|
return value
|
||||||
|
case .Text(let value):
|
||||||
|
return value
|
||||||
|
case .Comment(let value):
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ==(lhs:Token, rhs:Token) -> Bool {
|
public func ==(lhs:Token, rhs:Token) -> Bool {
|
||||||
switch (lhs, rhs) {
|
switch (lhs, rhs) {
|
||||||
case (.Text(let lhsValue), .Text(let rhsValue)):
|
case (.Text(let lhsValue), .Text(let rhsValue)):
|
||||||
return lhsValue == rhsValue
|
return lhsValue == rhsValue
|
||||||
case (.Variable(let lhsValue), .Variable(let rhsValue)):
|
case (.Variable(let lhsValue), .Variable(let rhsValue)):
|
||||||
return lhsValue == rhsValue
|
return lhsValue == rhsValue
|
||||||
case (.Block(let lhsValue), .Block(let rhsValue)):
|
case (.Block(let lhsValue), .Block(let rhsValue)):
|
||||||
return lhsValue == rhsValue
|
return lhsValue == rhsValue
|
||||||
case (.Comment(let lhsValue), .Comment(let rhsValue)):
|
case (.Comment(let lhsValue), .Comment(let rhsValue)):
|
||||||
return lhsValue == rhsValue
|
return lhsValue == rhsValue
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,51 +3,51 @@ import Foundation
|
|||||||
|
|
||||||
/// A structure used to represent a template variable, and to resolve it in a given context.
|
/// A structure used to represent a template variable, and to resolve it in a given context.
|
||||||
public struct Variable : Equatable {
|
public struct Variable : Equatable {
|
||||||
public let variable:String
|
public let variable:String
|
||||||
|
|
||||||
/// Create a variable with a string representing the variable
|
/// Create a variable with a string representing the variable
|
||||||
public init(_ variable:String) {
|
public init(_ variable:String) {
|
||||||
self.variable = variable
|
self.variable = variable
|
||||||
|
}
|
||||||
|
|
||||||
|
private func lookup() -> [String] {
|
||||||
|
return variable.componentsSeparatedByString(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve the variable in the given context
|
||||||
|
public func resolve(context:Context) -> AnyObject? {
|
||||||
|
var current:AnyObject? = context
|
||||||
|
|
||||||
|
if (variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\"")) {
|
||||||
|
return variable.substringWithRange(variable.startIndex.successor() ..< variable.endIndex.predecessor())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func lookup() -> [String] {
|
for bit in lookup() {
|
||||||
return variable.componentsSeparatedByString(".")
|
if let context = current as? Context {
|
||||||
}
|
current = context[bit]
|
||||||
|
} else if let dictionary = current as? Dictionary<String, AnyObject> {
|
||||||
/// Resolve the variable in the given context
|
current = dictionary[bit]
|
||||||
public func resolve(context:Context) -> AnyObject? {
|
} else if let array = current as? [AnyObject] {
|
||||||
var current:AnyObject? = context
|
if let index = bit.toInt() {
|
||||||
|
current = array[index]
|
||||||
if (variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\"")) {
|
} else if bit == "first" {
|
||||||
return variable.substringWithRange(variable.startIndex.successor() ..< variable.endIndex.predecessor())
|
current = array.first
|
||||||
|
} else if bit == "last" {
|
||||||
|
current = array.last
|
||||||
|
} else if bit == "count" {
|
||||||
|
current = count(array)
|
||||||
}
|
}
|
||||||
|
} else if let object = current as? NSObject {
|
||||||
for bit in lookup() {
|
current = object.valueForKey(bit)
|
||||||
if let context = current as? Context {
|
} else {
|
||||||
current = context[bit]
|
return nil
|
||||||
} else if let dictionary = current as? Dictionary<String, AnyObject> {
|
}
|
||||||
current = dictionary[bit]
|
|
||||||
} else if let array = current as? [AnyObject] {
|
|
||||||
if let index = bit.toInt() {
|
|
||||||
current = array[index]
|
|
||||||
} else if bit == "first" {
|
|
||||||
current = array.first
|
|
||||||
} else if bit == "last" {
|
|
||||||
current = array.last
|
|
||||||
} else if bit == "count" {
|
|
||||||
current = count(array)
|
|
||||||
}
|
|
||||||
} else if let object = current as? NSObject {
|
|
||||||
current = object.valueForKey(bit)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return current
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return current
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func ==(lhs:Variable, rhs:Variable) -> Bool {
|
public func ==(lhs:Variable, rhs:Variable) -> Bool {
|
||||||
return lhs.variable == rhs.variable
|
return lhs.variable == rhs.variable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,63 +3,63 @@ import XCTest
|
|||||||
import Stencil
|
import Stencil
|
||||||
|
|
||||||
class ContextTests: XCTestCase {
|
class ContextTests: XCTestCase {
|
||||||
var context:Context!
|
var context:Context!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
context = Context(dictionary: ["name": "Kyle"])
|
context = Context(dictionary: ["name": "Kyle"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToRetrieveAValue() {
|
func testItAllowsYouToRetrieveAValue() {
|
||||||
let name = context["name"] as! String
|
let name = context["name"] as! String
|
||||||
XCTAssertEqual(name, "Kyle")
|
XCTAssertEqual(name, "Kyle")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToSetValue() {
|
func testItAllowsYouToSetValue() {
|
||||||
context["name"] = "Katie"
|
context["name"] = "Katie"
|
||||||
|
|
||||||
let name = context["name"] as! String
|
let name = context["name"] as! String
|
||||||
XCTAssertEqual(name, "Katie")
|
XCTAssertEqual(name, "Katie")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToRemoveAValue() {
|
func testItAllowsYouToRemoveAValue() {
|
||||||
context["name"] = nil
|
context["name"] = nil
|
||||||
XCTAssertNil(context["name"])
|
XCTAssertNil(context["name"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToRetrieveAValueFromParent() {
|
func testItAllowsYouToRetrieveAValueFromParent() {
|
||||||
context.push()
|
context.push()
|
||||||
|
|
||||||
let name = context["name"] as! String
|
let name = context["name"] as! String
|
||||||
XCTAssertEqual(name, "Kyle")
|
XCTAssertEqual(name, "Kyle")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToOverideAParentVariable() {
|
func testItAllowsYouToOverideAParentVariable() {
|
||||||
context.push()
|
context.push()
|
||||||
context["name"] = "Katie"
|
context["name"] = "Katie"
|
||||||
|
|
||||||
let name = context["name"] as! String
|
let name = context["name"] as! String
|
||||||
XCTAssertEqual(name, "Katie")
|
XCTAssertEqual(name, "Katie")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testShowAllowYouToPopVariablesRestoringPreviousState() {
|
func testShowAllowYouToPopVariablesRestoringPreviousState() {
|
||||||
context.push()
|
context.push()
|
||||||
context["name"] = "Katie"
|
context["name"] = "Katie"
|
||||||
context.pop()
|
context.pop()
|
||||||
|
|
||||||
let name = context["name"] as! String
|
let name = context["name"] as! String
|
||||||
XCTAssertEqual(name, "Kyle")
|
XCTAssertEqual(name, "Kyle")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToPushADictionaryToTheStack() {
|
func testItAllowsYouToPushADictionaryToTheStack() {
|
||||||
context.push(["name": "Katie"])
|
context.push(["name": "Katie"])
|
||||||
|
|
||||||
let name = context["name"] as! String
|
let name = context["name"] as! String
|
||||||
XCTAssertEqual(name, "Katie")
|
XCTAssertEqual(name, "Katie")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItAllowsYouToCompareTwoContextsForEquality() {
|
func testItAllowsYouToCompareTwoContextsForEquality() {
|
||||||
let otherContext = Context(dictionary: ["name": "Kyle"])
|
let otherContext = Context(dictionary: ["name": "Kyle"])
|
||||||
|
|
||||||
XCTAssertEqual(otherContext, context)
|
XCTAssertEqual(otherContext, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,47 +4,47 @@ import Stencil
|
|||||||
|
|
||||||
class LexerTests: XCTestCase {
|
class LexerTests: XCTestCase {
|
||||||
|
|
||||||
func testTokenizeText() {
|
func testTokenizeText() {
|
||||||
let lexer = Lexer(templateString:"Hello World")
|
let lexer = Lexer(templateString:"Hello World")
|
||||||
let tokens = lexer.tokenize()
|
let tokens = lexer.tokenize()
|
||||||
|
|
||||||
XCTAssertEqual(tokens.count, 1)
|
XCTAssertEqual(tokens.count, 1)
|
||||||
XCTAssertEqual(tokens.first!, Token.Text(value: "Hello World"))
|
XCTAssertEqual(tokens.first!, Token.Text(value: "Hello World"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTokenizeComment() {
|
func testTokenizeComment() {
|
||||||
let lexer = Lexer(templateString:"{# Comment #}")
|
let lexer = Lexer(templateString:"{# Comment #}")
|
||||||
let tokens = lexer.tokenize()
|
let tokens = lexer.tokenize()
|
||||||
|
|
||||||
XCTAssertEqual(tokens.count, 1)
|
XCTAssertEqual(tokens.count, 1)
|
||||||
XCTAssertEqual(tokens.first!, Token.Comment(value: "Comment"))
|
XCTAssertEqual(tokens.first!, Token.Comment(value: "Comment"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTokenizeVariable() {
|
func testTokenizeVariable() {
|
||||||
let lexer = Lexer(templateString:"{{ Variable }}")
|
let lexer = Lexer(templateString:"{{ Variable }}")
|
||||||
let tokens = lexer.tokenize()
|
let tokens = lexer.tokenize()
|
||||||
|
|
||||||
XCTAssertEqual(tokens.count, 1)
|
XCTAssertEqual(tokens.count, 1)
|
||||||
XCTAssertEqual(tokens.first!, Token.Variable(value: "Variable"))
|
XCTAssertEqual(tokens.first!, Token.Variable(value: "Variable"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTokenizeMixture() {
|
func testTokenizeMixture() {
|
||||||
let lexer = Lexer(templateString:"My name is {{ name }}.")
|
let lexer = Lexer(templateString:"My name is {{ name }}.")
|
||||||
let tokens = lexer.tokenize()
|
let tokens = lexer.tokenize()
|
||||||
|
|
||||||
XCTAssertEqual(tokens.count, 3)
|
XCTAssertEqual(tokens.count, 3)
|
||||||
XCTAssertEqual(tokens[0], Token.Text(value: "My name is "))
|
XCTAssertEqual(tokens[0], Token.Text(value: "My name is "))
|
||||||
XCTAssertEqual(tokens[1], Token.Variable(value: "name"))
|
XCTAssertEqual(tokens[1], Token.Variable(value: "name"))
|
||||||
XCTAssertEqual(tokens[2], Token.Text(value: "."))
|
XCTAssertEqual(tokens[2], Token.Text(value: "."))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTokenizeTwoVariables() { // Don't be greedy
|
func testTokenizeTwoVariables() { // Don't be greedy
|
||||||
let lexer = Lexer(templateString:"{{ thing }}{{ name }}")
|
let lexer = Lexer(templateString:"{{ thing }}{{ name }}")
|
||||||
let tokens = lexer.tokenize()
|
let tokens = lexer.tokenize()
|
||||||
|
|
||||||
XCTAssertEqual(tokens.count, 2)
|
XCTAssertEqual(tokens.count, 2)
|
||||||
XCTAssertEqual(tokens[0], Token.Variable(value: "thing"))
|
XCTAssertEqual(tokens[0], Token.Variable(value: "thing"))
|
||||||
XCTAssertEqual(tokens[1], Token.Variable(value: "name"))
|
XCTAssertEqual(tokens[1], Token.Variable(value: "name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,248 +3,248 @@ import XCTest
|
|||||||
import Stencil
|
import Stencil
|
||||||
|
|
||||||
class ErrorNodeError : Error {
|
class ErrorNodeError : Error {
|
||||||
var description: String {
|
var description: String {
|
||||||
return "Node Error"
|
return "Node Error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorNode : Node {
|
class ErrorNode : Node {
|
||||||
func render(context: Context) -> Result {
|
func render(context: Context) -> Result {
|
||||||
|
|
||||||
return .Error(ErrorNodeError())
|
return .Error(ErrorNodeError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeTests: XCTestCase {
|
class NodeTests: XCTestCase {
|
||||||
var context:Context!
|
var context:Context!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
context = Context(dictionary: [
|
context = Context(dictionary: [
|
||||||
"name": "Kyle",
|
"name": "Kyle",
|
||||||
"age": 27,
|
"age": 27,
|
||||||
"items": [1,2,3],
|
"items": [1,2,3],
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextNodeTests: NodeTests {
|
class TextNodeTests: NodeTests {
|
||||||
func testTextNodeResolvesText() {
|
func testTextNodeResolvesText() {
|
||||||
let node = TextNode(text:"Hello World")
|
let node = TextNode(text:"Hello World")
|
||||||
let result = node.render(context)
|
let result = node.render(context)
|
||||||
|
|
||||||
switch node.render(context) {
|
switch node.render(context) {
|
||||||
case .Success(let string):
|
case .Success(let string):
|
||||||
XCTAssertEqual(string, "Hello World")
|
XCTAssertEqual(string, "Hello World")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VariableNodeTests: NodeTests {
|
class VariableNodeTests: NodeTests {
|
||||||
func testVariableNodeResolvesVariable() {
|
func testVariableNodeResolvesVariable() {
|
||||||
let node = VariableNode(variable:Variable("name"))
|
let node = VariableNode(variable:Variable("name"))
|
||||||
let result = node.render(context)
|
let result = node.render(context)
|
||||||
|
|
||||||
switch node.render(context) {
|
switch node.render(context) {
|
||||||
case .Success(let string):
|
case .Success(let string):
|
||||||
XCTAssertEqual(string, "Kyle")
|
XCTAssertEqual(string, "Kyle")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testVariableNodeResolvesNonStringVariable() {
|
func testVariableNodeResolvesNonStringVariable() {
|
||||||
let node = VariableNode(variable:Variable("age"))
|
let node = VariableNode(variable:Variable("age"))
|
||||||
let result = node.render(context)
|
let result = node.render(context)
|
||||||
|
|
||||||
switch node.render(context) {
|
switch node.render(context) {
|
||||||
case .Success(let string):
|
case .Success(let string):
|
||||||
XCTAssertEqual(string, "27")
|
XCTAssertEqual(string, "27")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderNodeTests: NodeTests {
|
class RenderNodeTests: NodeTests {
|
||||||
func testRenderingNodes() {
|
func testRenderingNodes() {
|
||||||
let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name")] as [Node]
|
let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name")] as [Node]
|
||||||
switch renderNodes(nodes, context) {
|
switch renderNodes(nodes, context) {
|
||||||
case .Success(let result):
|
case .Success(let result):
|
||||||
XCTAssertEqual(result, "Hello Kyle")
|
XCTAssertEqual(result, "Hello Kyle")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testRenderingNodesWithFailure() {
|
func testRenderingNodesWithFailure() {
|
||||||
let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name"), ErrorNode()] as [Node]
|
let nodes = [TextNode(text:"Hello "), VariableNode(variable: "name"), ErrorNode()] as [Node]
|
||||||
|
|
||||||
switch renderNodes(nodes, context) {
|
switch renderNodes(nodes, context) {
|
||||||
case .Success(let result):
|
case .Success(let result):
|
||||||
XCTAssert(false, "Unexpected success")
|
XCTAssert(false, "Unexpected success")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssertEqual("\(error)", "Node Error")
|
XCTAssertEqual("\(error)", "Node Error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForNodeTests: NodeTests {
|
class ForNodeTests: NodeTests {
|
||||||
func testForNodeRender() {
|
func testForNodeRender() {
|
||||||
let node = ForNode(variable: "items", loopVariable: "item", nodes: [VariableNode(variable: "item")], emptyNodes:[])
|
let node = ForNode(variable: "items", loopVariable: "item", nodes: [VariableNode(variable: "item")], emptyNodes:[])
|
||||||
let result = node.render(context)
|
let result = node.render(context)
|
||||||
|
|
||||||
switch node.render(context) {
|
switch node.render(context) {
|
||||||
case .Success(let string):
|
case .Success(let string):
|
||||||
XCTAssertEqual(string, "123")
|
XCTAssertEqual(string, "123")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IfNodeTests: NodeTests {
|
class IfNodeTests: NodeTests {
|
||||||
|
|
||||||
// MARK: Parsing
|
// MARK: Parsing
|
||||||
|
|
||||||
func testParseIf() {
|
func testParseIf() {
|
||||||
let tokens = [
|
let tokens = [
|
||||||
Token.Block(value: "if value"),
|
Token.Block(value: "if value"),
|
||||||
Token.Text(value: "true"),
|
Token.Text(value: "true"),
|
||||||
Token.Block(value: "else"),
|
Token.Block(value: "else"),
|
||||||
Token.Text(value: "false"),
|
Token.Text(value: "false"),
|
||||||
Token.Block(value: "endif")
|
Token.Block(value: "endif")
|
||||||
]
|
]
|
||||||
|
|
||||||
let parser = TokenParser(tokens: tokens)
|
let parser = TokenParser(tokens: tokens)
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
let node = nodes.first as! IfNode
|
let node = nodes.first as! IfNode
|
||||||
let trueNode = node.trueNodes.first as! TextNode
|
let trueNode = node.trueNodes.first as! TextNode
|
||||||
let falseNode = node.falseNodes.first as! TextNode
|
let falseNode = node.falseNodes.first as! TextNode
|
||||||
|
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
XCTAssertEqual(node.variable.variable, "value")
|
XCTAssertEqual(node.variable.variable, "value")
|
||||||
XCTAssertEqual(node.trueNodes.count, 1)
|
XCTAssertEqual(node.trueNodes.count, 1)
|
||||||
XCTAssertEqual(trueNode.text, "true")
|
XCTAssertEqual(trueNode.text, "true")
|
||||||
XCTAssertEqual(node.falseNodes.count, 1)
|
XCTAssertEqual(node.falseNodes.count, 1)
|
||||||
XCTAssertEqual(falseNode.text, "false")
|
XCTAssertEqual(falseNode.text, "false")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParseIfNot() {
|
func testParseIfNot() {
|
||||||
let tokens = [
|
let tokens = [
|
||||||
Token.Block(value: "ifnot value"),
|
Token.Block(value: "ifnot value"),
|
||||||
Token.Text(value: "false"),
|
Token.Text(value: "false"),
|
||||||
Token.Block(value: "else"),
|
Token.Block(value: "else"),
|
||||||
Token.Text(value: "true"),
|
Token.Text(value: "true"),
|
||||||
Token.Block(value: "endif")
|
Token.Block(value: "endif")
|
||||||
]
|
]
|
||||||
|
|
||||||
let parser = TokenParser(tokens: tokens)
|
let parser = TokenParser(tokens: tokens)
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
let node = nodes.first as! IfNode
|
let node = nodes.first as! IfNode
|
||||||
let trueNode = node.trueNodes.first as! TextNode
|
let trueNode = node.trueNodes.first as! TextNode
|
||||||
let falseNode = node.falseNodes.first as! TextNode
|
let falseNode = node.falseNodes.first as! TextNode
|
||||||
|
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
XCTAssertEqual(node.variable.variable, "value")
|
XCTAssertEqual(node.variable.variable, "value")
|
||||||
XCTAssertEqual(node.trueNodes.count, 1)
|
XCTAssertEqual(node.trueNodes.count, 1)
|
||||||
XCTAssertEqual(trueNode.text, "true")
|
XCTAssertEqual(trueNode.text, "true")
|
||||||
XCTAssertEqual(node.falseNodes.count, 1)
|
XCTAssertEqual(node.falseNodes.count, 1)
|
||||||
XCTAssertEqual(falseNode.text, "false")
|
XCTAssertEqual(falseNode.text, "false")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParseIfWithoutEndIfError() {
|
func testParseIfWithoutEndIfError() {
|
||||||
let tokens = [
|
let tokens = [
|
||||||
Token.Block(value: "if value"),
|
Token.Block(value: "if value"),
|
||||||
]
|
]
|
||||||
|
|
||||||
let parser = TokenParser(tokens: tokens)
|
let parser = TokenParser(tokens: tokens)
|
||||||
assertFailure(parser.parse(), "if: `endif` was not found.")
|
assertFailure(parser.parse(), "if: `endif` was not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParseIfNotWithoutEndIfError() {
|
||||||
|
let tokens = [
|
||||||
|
Token.Block(value: "ifnot value"),
|
||||||
|
]
|
||||||
|
|
||||||
|
let parser = TokenParser(tokens: tokens)
|
||||||
|
assertFailure(parser.parse(), "ifnot: `endif` was not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Rendering
|
||||||
|
|
||||||
|
func testIfNodeRenderTruth() {
|
||||||
|
let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")])
|
||||||
|
let result = node.render(context)
|
||||||
|
|
||||||
|
switch node.render(context) {
|
||||||
|
case .Success(let string):
|
||||||
|
XCTAssertEqual(string, "true")
|
||||||
|
case .Error(let error):
|
||||||
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParseIfNotWithoutEndIfError() {
|
func testIfNodeRenderFalse() {
|
||||||
let tokens = [
|
let node = IfNode(variable: "unknown", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")])
|
||||||
Token.Block(value: "ifnot value"),
|
let result = node.render(context)
|
||||||
]
|
|
||||||
|
|
||||||
let parser = TokenParser(tokens: tokens)
|
switch node.render(context) {
|
||||||
assertFailure(parser.parse(), "ifnot: `endif` was not found.")
|
case .Success(let string):
|
||||||
}
|
XCTAssertEqual(string, "false")
|
||||||
|
case .Error(let error):
|
||||||
// MARK: Rendering
|
XCTAssert(false, "Unexpected error")
|
||||||
|
|
||||||
func testIfNodeRenderTruth() {
|
|
||||||
let node = IfNode(variable: "items", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")])
|
|
||||||
let result = node.render(context)
|
|
||||||
|
|
||||||
switch node.render(context) {
|
|
||||||
case .Success(let string):
|
|
||||||
XCTAssertEqual(string, "true")
|
|
||||||
case .Error(let error):
|
|
||||||
XCTAssert(false, "Unexpected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIfNodeRenderFalse() {
|
|
||||||
let node = IfNode(variable: "unknown", trueNodes: [TextNode(text: "true")], falseNodes: [TextNode(text: "false")])
|
|
||||||
let result = node.render(context)
|
|
||||||
|
|
||||||
switch node.render(context) {
|
|
||||||
case .Success(let string):
|
|
||||||
XCTAssertEqual(string, "false")
|
|
||||||
case .Error(let error):
|
|
||||||
XCTAssert(false, "Unexpected error")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NowNodeTests: NodeTests {
|
class NowNodeTests: NodeTests {
|
||||||
|
|
||||||
// MARK: Parsing
|
// MARK: Parsing
|
||||||
|
|
||||||
func testParseDefaultNow() {
|
func testParseDefaultNow() {
|
||||||
let tokens = [ Token.Block(value: "now") ]
|
let tokens = [ Token.Block(value: "now") ]
|
||||||
let parser = TokenParser(tokens: tokens)
|
let parser = TokenParser(tokens: tokens)
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
let node = nodes.first as! NowNode
|
let node = nodes.first as! NowNode
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"")
|
XCTAssertEqual(node.format.variable, "\"yyyy-MM-dd 'at' HH:mm\"")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParseNowWithFormat() {
|
func testParseNowWithFormat() {
|
||||||
let tokens = [ Token.Block(value: "now \"HH:mm\"") ]
|
let tokens = [ Token.Block(value: "now \"HH:mm\"") ]
|
||||||
let parser = TokenParser(tokens: tokens)
|
let parser = TokenParser(tokens: tokens)
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
let node = nodes.first as! NowNode
|
let node = nodes.first as! NowNode
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
XCTAssertEqual(node.format.variable, "\"HH:mm\"")
|
XCTAssertEqual(node.format.variable, "\"HH:mm\"")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Rendering
|
// MARK: Rendering
|
||||||
|
|
||||||
func testRenderNowNode() {
|
func testRenderNowNode() {
|
||||||
let node = NowNode(format: Variable("\"yyyy-MM-dd\""))
|
let node = NowNode(format: Variable("\"yyyy-MM-dd\""))
|
||||||
let result = node.render(context)
|
let result = node.render(context)
|
||||||
|
|
||||||
let formatter = NSDateFormatter()
|
let formatter = NSDateFormatter()
|
||||||
formatter.dateFormat = "yyyy-MM-dd"
|
formatter.dateFormat = "yyyy-MM-dd"
|
||||||
let date = formatter.stringFromDate(NSDate())
|
let date = formatter.stringFromDate(NSDate())
|
||||||
|
|
||||||
switch node.render(context) {
|
switch node.render(context) {
|
||||||
case .Success(let string):
|
case .Success(let string):
|
||||||
XCTAssertEqual(string, date)
|
XCTAssertEqual(string, date)
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,47 +3,47 @@ import XCTest
|
|||||||
import Stencil
|
import Stencil
|
||||||
|
|
||||||
class TokenParserTests: XCTestCase {
|
class TokenParserTests: XCTestCase {
|
||||||
func testParsingTextToken() {
|
func testParsingTextToken() {
|
||||||
let parser = TokenParser(tokens: [
|
let parser = TokenParser(tokens: [
|
||||||
Token.Text(value: "Hello World")
|
Token.Text(value: "Hello World")
|
||||||
])
|
])
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
let node = nodes.first as! TextNode
|
let node = nodes.first as! TextNode
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
XCTAssertEqual(node.text, "Hello World")
|
XCTAssertEqual(node.text, "Hello World")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParsingVariableToken() {
|
func testParsingVariableToken() {
|
||||||
let parser = TokenParser(tokens: [
|
let parser = TokenParser(tokens: [
|
||||||
Token.Variable(value: "name")
|
Token.Variable(value: "name")
|
||||||
])
|
])
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
let node = nodes.first as! VariableNode
|
let node = nodes.first as! VariableNode
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
XCTAssertEqual(node.variable, Variable("name"))
|
XCTAssertEqual(node.variable, Variable("name"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParsingCommentToken() {
|
func testParsingCommentToken() {
|
||||||
let parser = TokenParser(tokens: [
|
let parser = TokenParser(tokens: [
|
||||||
Token.Comment(value: "Secret stuff!")
|
Token.Comment(value: "Secret stuff!")
|
||||||
])
|
])
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
XCTAssertEqual(nodes.count, 0)
|
XCTAssertEqual(nodes.count, 0)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParsingTagToken() {
|
func testParsingTagToken() {
|
||||||
let parser = TokenParser(tokens: [
|
let parser = TokenParser(tokens: [
|
||||||
Token.Block(value: "now"),
|
Token.Block(value: "now"),
|
||||||
])
|
])
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
assertSuccess(parser.parse()) { nodes in
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssertEqual(nodes.count, 1)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,77 +3,77 @@ import XCTest
|
|||||||
import Stencil
|
import Stencil
|
||||||
|
|
||||||
func assertSuccess(result:TokenParser.Results, block:(([Node]) -> ())) {
|
func assertSuccess(result:TokenParser.Results, block:(([Node]) -> ())) {
|
||||||
switch result {
|
switch result {
|
||||||
case .Success(let nodes):
|
case .Success(let nodes):
|
||||||
block(nodes)
|
block(nodes)
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertFailure(result:TokenParser.Results, description:String) {
|
func assertFailure(result:TokenParser.Results, description:String) {
|
||||||
switch result {
|
switch result {
|
||||||
case .Success(let nodes):
|
case .Success(let nodes):
|
||||||
XCTAssert(false, "Unexpected error")
|
XCTAssert(false, "Unexpected error")
|
||||||
case .Error(let error):
|
case .Error(let error):
|
||||||
XCTAssertEqual("\(error)", description)
|
XCTAssertEqual("\(error)", description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomNode : Node {
|
class CustomNode : Node {
|
||||||
func render(context:Context) -> Result {
|
func render(context:Context) -> Result {
|
||||||
return .Success("Hello World")
|
return .Success("Hello World")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StencilTests: XCTestCase {
|
class StencilTests: XCTestCase {
|
||||||
func testReadmeExample() {
|
func testReadmeExample() {
|
||||||
let templateString = "There are {{ articles.count }} articles.\n" +
|
let templateString = "There are {{ articles.count }} articles.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"{% for article in articles %}" +
|
"{% for article in articles %}" +
|
||||||
" - {{ article.title }} by {{ article.author }}.\n" +
|
" - {{ article.title }} by {{ article.author }}.\n" +
|
||||||
"{% endfor %}\n"
|
"{% endfor %}\n"
|
||||||
|
|
||||||
let context = Context(dictionary: [
|
let context = Context(dictionary: [
|
||||||
"articles": [
|
"articles": [
|
||||||
[ "title": "Migrating from OCUnit to XCTest", "author": "Kyle Fuller" ],
|
[ "title": "Migrating from OCUnit to XCTest", "author": "Kyle Fuller" ],
|
||||||
[ "title": "Memory Management with ARC", "author": "Kyle Fuller" ],
|
[ "title": "Memory Management with ARC", "author": "Kyle Fuller" ],
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
|
|
||||||
let template = Template(templateString:templateString)
|
let template = Template(templateString:templateString)
|
||||||
let result = template.render(context)
|
let result = template.render(context)
|
||||||
|
|
||||||
let fixture = "There are 2 articles.\n" +
|
let fixture = "There are 2 articles.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
" - Migrating from OCUnit to XCTest by Kyle Fuller.\n" +
|
" - Migrating from OCUnit to XCTest by Kyle Fuller.\n" +
|
||||||
" - Memory Management with ARC by Kyle Fuller.\n" +
|
" - Memory Management with ARC by Kyle Fuller.\n" +
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
XCTAssertEqual(result, Result.Success(fixture))
|
XCTAssertEqual(result, Result.Success(fixture))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCustomTag() {
|
||||||
|
let templateString = "{% custom %}"
|
||||||
|
let template = Template(templateString:templateString)
|
||||||
|
|
||||||
|
template.parser.registerTag("custom") { parser, token in
|
||||||
|
return .Success(node:CustomNode())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCustomTag() {
|
let result = template.render()
|
||||||
let templateString = "{% custom %}"
|
XCTAssertEqual(result, Result.Success("Hello World"))
|
||||||
let template = Template(templateString:templateString)
|
}
|
||||||
|
|
||||||
template.parser.registerTag("custom") { parser, token in
|
func testSimpleCustomTag() {
|
||||||
return .Success(node:CustomNode())
|
let templateString = "{% custom %}"
|
||||||
}
|
let template = Template(templateString:templateString)
|
||||||
|
|
||||||
let result = template.render()
|
template.parser.registerSimpleTag("custom") { context in
|
||||||
XCTAssertEqual(result, Result.Success("Hello World"))
|
return .Success("Hello World")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSimpleCustomTag() {
|
let result = template.render()
|
||||||
let templateString = "{% custom %}"
|
XCTAssertEqual(result, Result.Success("Hello World"))
|
||||||
let template = Template(templateString:templateString)
|
}
|
||||||
|
|
||||||
template.parser.registerSimpleTag("custom") { context in
|
|
||||||
return .Success("Hello World")
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = template.render()
|
|
||||||
XCTAssertEqual(result, Result.Success("Hello World"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,71 +5,71 @@ import PathKit
|
|||||||
|
|
||||||
class IncludeTests: NodeTests {
|
class IncludeTests: NodeTests {
|
||||||
|
|
||||||
var loader:TemplateLoader!
|
var loader:TemplateLoader!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
|
||||||
let path = (Path(__FILE__) + Path("../..")).absolute()
|
let path = (Path(__FILE__) + Path("../..")).absolute()
|
||||||
loader = TemplateLoader(paths: [path])
|
loader = TemplateLoader(paths: [path])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Parsing
|
||||||
|
|
||||||
|
func testParseMissingTemplate() {
|
||||||
|
let tokens = [ Token.Block(value: "include") ]
|
||||||
|
let parser = TokenParser(tokens: tokens)
|
||||||
|
|
||||||
|
assertFailure(parser.parse(), "include: Tag takes one argument, the template file to be included")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParse() {
|
||||||
|
let tokens = [ Token.Block(value: "include \"test.html\"") ]
|
||||||
|
let parser = TokenParser(tokens: tokens)
|
||||||
|
|
||||||
|
assertSuccess(parser.parse()) { nodes in
|
||||||
|
let node = nodes.first as! IncludeNode
|
||||||
|
XCTAssertEqual(nodes.count, 1)
|
||||||
|
XCTAssertEqual(node.templateName, "test.html")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Parsing
|
// MARK: Render
|
||||||
|
|
||||||
func testParseMissingTemplate() {
|
func testRenderWithoutLoader() {
|
||||||
let tokens = [ Token.Block(value: "include") ]
|
let node = IncludeNode(templateName: "test.html")
|
||||||
let parser = TokenParser(tokens: tokens)
|
let result = node.render(Context())
|
||||||
|
|
||||||
assertFailure(parser.parse(), "include: Tag takes one argument, the template file to be included")
|
switch result {
|
||||||
|
case .Success(let string):
|
||||||
|
XCTAssert(false, "Unexpected error")
|
||||||
|
case .Error(let error):
|
||||||
|
XCTAssertEqual("\(error)", "Template loader not in context")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testParse() {
|
func testRenderWithoutTemplateNamed() {
|
||||||
let tokens = [ Token.Block(value: "include \"test.html\"") ]
|
let node = IncludeNode(templateName: "unknown.html")
|
||||||
let parser = TokenParser(tokens: tokens)
|
let result = node.render(Context(dictionary:["loader":loader]))
|
||||||
|
|
||||||
assertSuccess(parser.parse()) { nodes in
|
switch result {
|
||||||
let node = nodes.first as! IncludeNode
|
case .Success(let string):
|
||||||
XCTAssertEqual(nodes.count, 1)
|
XCTAssert(false, "Unexpected error")
|
||||||
XCTAssertEqual(node.templateName, "test.html")
|
case .Error(let error):
|
||||||
}
|
XCTAssertTrue("\(error)".hasPrefix("Template 'unknown.html' not found"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Render
|
func testRender() {
|
||||||
|
let node = IncludeNode(templateName: "test.html")
|
||||||
|
let result = node.render(Context(dictionary:["loader":loader, "target": "World"]))
|
||||||
|
|
||||||
func testRenderWithoutLoader() {
|
switch result {
|
||||||
let node = IncludeNode(templateName: "test.html")
|
case .Success(let string):
|
||||||
let result = node.render(Context())
|
XCTAssertEqual(string, "Hello World!")
|
||||||
|
case .Error(let error):
|
||||||
switch result {
|
XCTAssert(false, "Unexpected error: \(error)")
|
||||||
case .Success(let string):
|
|
||||||
XCTAssert(false, "Unexpected error")
|
|
||||||
case .Error(let error):
|
|
||||||
XCTAssertEqual("\(error)", "Template loader not in context")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRenderWithoutTemplateNamed() {
|
|
||||||
let node = IncludeNode(templateName: "unknown.html")
|
|
||||||
let result = node.render(Context(dictionary:["loader":loader]))
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
case .Success(let string):
|
|
||||||
XCTAssert(false, "Unexpected error")
|
|
||||||
case .Error(let error):
|
|
||||||
XCTAssertTrue("\(error)".hasPrefix("Template 'unknown.html' not found"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRender() {
|
|
||||||
let node = IncludeNode(templateName: "test.html")
|
|
||||||
let result = node.render(Context(dictionary:["loader":loader, "target": "World"]))
|
|
||||||
|
|
||||||
switch result {
|
|
||||||
case .Success(let string):
|
|
||||||
XCTAssertEqual(string, "Hello World!")
|
|
||||||
case .Error(let error):
|
|
||||||
XCTAssert(false, "Unexpected error: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,20 @@ import PathKit
|
|||||||
|
|
||||||
class TemplateLoaderTests: XCTestCase {
|
class TemplateLoaderTests: XCTestCase {
|
||||||
|
|
||||||
func testLoadingUnknownTemplate() {
|
func testLoadingUnknownTemplate() {
|
||||||
let loader = TemplateLoader(paths:[])
|
let loader = TemplateLoader(paths:[])
|
||||||
XCTAssertNil(loader.loadTemplate("unknown.html"))
|
XCTAssertNil(loader.loadTemplate("unknown.html"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLoadingUnknownTemplates() {
|
func testLoadingUnknownTemplates() {
|
||||||
let loader = TemplateLoader(paths:[])
|
let loader = TemplateLoader(paths:[])
|
||||||
XCTAssertNil(loader.loadTemplate(["unknown.html", "unknown2.html"]))
|
XCTAssertNil(loader.loadTemplate(["unknown.html", "unknown2.html"]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLoadingTemplate() {
|
func testLoadingTemplate() {
|
||||||
let path = (Path(__FILE__) + Path("..")).absolute()
|
let path = (Path(__FILE__) + Path("..")).absolute()
|
||||||
let loader = TemplateLoader(paths: [path])
|
let loader = TemplateLoader(paths: [path])
|
||||||
XCTAssertTrue(loader.loadTemplate("test.html") != nil)
|
XCTAssertTrue(loader.loadTemplate("test.html") != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import Stencil
|
|||||||
|
|
||||||
class TemplateTests: XCTestCase {
|
class TemplateTests: XCTestCase {
|
||||||
|
|
||||||
func testTemplate() {
|
func testTemplate() {
|
||||||
let context = Context(dictionary: [ "name": "Kyle" ])
|
let context = Context(dictionary: [ "name": "Kyle" ])
|
||||||
let template = Template(templateString: "Hello World")
|
let template = Template(templateString: "Hello World")
|
||||||
let result = template.render(context)
|
let result = template.render(context)
|
||||||
XCTAssertEqual(result, Result.Success("Hello World"))
|
XCTAssertEqual(result, Result.Success("Hello World"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,60 +3,60 @@ import XCTest
|
|||||||
import Stencil
|
import Stencil
|
||||||
|
|
||||||
@objc class Object : NSObject {
|
@objc class Object : NSObject {
|
||||||
let title = "Hello World"
|
let title = "Hello World"
|
||||||
}
|
}
|
||||||
|
|
||||||
class VariableTests: XCTestCase {
|
class VariableTests: XCTestCase {
|
||||||
var context:Context!
|
var context:Context!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
context = Context(dictionary: [
|
context = Context(dictionary: [
|
||||||
"name": "Kyle",
|
"name": "Kyle",
|
||||||
"contacts": [ "Katie", "Orta", ],
|
"contacts": [ "Katie", "Orta", ],
|
||||||
"profiles": [ "github": "kylef", ],
|
"profiles": [ "github": "kylef", ],
|
||||||
"object": Object(),
|
"object": Object(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingStringLiteral() {
|
func testResolvingStringLiteral() {
|
||||||
let variable = Variable("\"name\"")
|
let variable = Variable("\"name\"")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "name")
|
XCTAssertEqual(result, "name")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingVariable() {
|
func testResolvingVariable() {
|
||||||
let variable = Variable("name")
|
let variable = Variable("name")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "Kyle")
|
XCTAssertEqual(result, "Kyle")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingItemFromDictionary() {
|
func testResolvingItemFromDictionary() {
|
||||||
let variable = Variable("profiles.github")
|
let variable = Variable("profiles.github")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "kylef")
|
XCTAssertEqual(result, "kylef")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingItemFromArrayWithIndex() {
|
func testResolvingItemFromArrayWithIndex() {
|
||||||
let variable = Variable("contacts.0")
|
let variable = Variable("contacts.0")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "Katie")
|
XCTAssertEqual(result, "Katie")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingFirstItemFromArray() {
|
func testResolvingFirstItemFromArray() {
|
||||||
let variable = Variable("contacts.first")
|
let variable = Variable("contacts.first")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "Katie")
|
XCTAssertEqual(result, "Katie")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingLastItemFromArray() {
|
func testResolvingLastItemFromArray() {
|
||||||
let variable = Variable("contacts.last")
|
let variable = Variable("contacts.last")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "Orta")
|
XCTAssertEqual(result, "Orta")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testResolvingValueViaKVO() {
|
func testResolvingValueViaKVO() {
|
||||||
let variable = Variable("object.title")
|
let variable = Variable("object.title")
|
||||||
let result = variable.resolve(context) as! String
|
let result = variable.resolve(context) as! String
|
||||||
XCTAssertEqual(result, "Hello World")
|
XCTAssertEqual(result, "Hello World")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user