refactor: TemplateLoader to protocol, follow Swift API guidelines

This commit is contained in:
Kyle Fuller
2016-11-28 03:50:53 +00:00
parent 5ca1b78854
commit 429290e0b7
9 changed files with 65 additions and 34 deletions

View File

@@ -4,6 +4,12 @@
### Breaking
- `TemplateLoader` is now a protocol with the file system based loader now
called `FileSystemLoader`. You will need to use `FileSystemLoader` instead.
- `TemplateLoader` `loadTemplate` methods are now throwing and now take labels
for the `name` and `names` arguments.
- Many internal classes are no longer private. Some APIs were previously
accessible due to earlier versions of Swift requiring the types to be public
to be able to test. Now we have access to `@testable` these can correctly be

View File

@@ -27,9 +27,8 @@ class IncludeNode : NodeType {
throw TemplateSyntaxError("'\(self.templateName)' could not be resolved as a string")
}
guard let template = loader.loadTemplate(templateName) else {
let paths = loader.paths.map { $0.description }.joined(separator: ", ")
throw TemplateSyntaxError("'\(templateName)' template not found in \(paths)")
guard let template = try loader.loadTemplate(name: templateName) else {
throw TemplateSyntaxError("'\(templateName)' template not found")
}
return try template.render(context)

View File

@@ -67,9 +67,8 @@ class ExtendsNode : NodeType {
throw TemplateSyntaxError("'\(self.templateName)' could not be resolved as a string")
}
guard let template = loader.loadTemplate(templateName) else {
let paths: String = loader.paths.map { $0.description }.joined(separator: ", ")
throw TemplateSyntaxError("'\(templateName)' template not found in \(paths)")
guard let template = try loader.loadTemplate(name: templateName) else {
throw TemplateSyntaxError("'\(templateName)' template not found")
}
let blockContext: BlockContext

View File

@@ -9,6 +9,12 @@ let NSFileNoSuchFileError = 4
public class Template: ExpressibleByStringLiteral {
let tokens: [Token]
/// Create a template with a template string
public init(templateString: String) {
let lexer = Lexer(templateString: templateString)
tokens = lexer.tokenize()
}
/// Create a template with the given name inside the given bundle
public convenience init(named:String, inBundle bundle:Bundle? = nil) throws {
let useBundle = bundle ?? Bundle.main
@@ -25,28 +31,22 @@ public class Template: ExpressibleByStringLiteral {
}
/// Create a template with a file found at the given path
public convenience init(path:Path) throws {
public convenience init(path: Path) throws {
self.init(templateString: try path.read())
}
/// Create a template with a template string
public init(templateString:String) {
let lexer = Lexer(templateString: templateString)
tokens = lexer.tokenize()
}
// Create a template with a template string literal
public convenience required init(stringLiteral value:String) {
public convenience required init(stringLiteral value: String) {
self.init(templateString: value)
}
// Create a template with a template string literal
public convenience required init(extendedGraphemeClusterLiteral value:StringLiteralType) {
public convenience required init(extendedGraphemeClusterLiteral value: StringLiteralType) {
self.init(stringLiteral: value)
}
// Create a template with a template string literal
public convenience required init(unicodeScalarLiteral value:StringLiteralType) {
public convenience required init(unicodeScalarLiteral value: StringLiteralType) {
self.init(stringLiteral: value)
}

View File

@@ -2,8 +2,29 @@ import Foundation
import PathKit
public protocol TemplateLoader {
func loadTemplate(name: String) throws -> Template?
func loadTemplate(names: [String]) throws -> Template?
}
extension TemplateLoader {
func loadTemplate(names: [String]) throws -> Template? {
for name in names {
let template = try loadTemplate(name: name)
if template != nil {
return template
}
}
return nil
}
}
// A class for loading a template from disk
public class TemplateLoader {
public class FileSystemLoader: TemplateLoader {
public let paths: [Path]
public init(paths: [Path]) {
@@ -16,19 +37,25 @@ public class TemplateLoader {
}
}
public func loadTemplate(_ templateName: String) -> Template? {
return loadTemplate([templateName])
public func loadTemplate(name: String) throws -> Template? {
for path in paths {
let templatePath = path + Path(name)
if templatePath.exists {
return try Template(path: templatePath)
}
}
public func loadTemplate(_ templateNames: [String]) -> Template? {
return nil
}
public func loadTemplate(names: [String]) throws -> Template? {
for path in paths {
for templateName in templateNames {
for templateName in names {
let templatePath = path + Path(templateName)
if templatePath.exists {
if let template = try? Template(path: templatePath) {
return template
}
return try Template(path: templatePath)
}
}
}

View File

@@ -6,7 +6,7 @@ import PathKit
func testInclude() {
describe("Include") {
let path = Path(#file) + ".." + "fixtures"
let loader = TemplateLoader(paths: [path])
let loader = FileSystemLoader(paths: [path])
$0.describe("parsing") {
$0.it("throws an error when no template is given") {

View File

@@ -6,17 +6,17 @@ import PathKit
func testInheritence() {
describe("Inheritence") {
let path = Path(#file) + ".." + "fixtures"
let loader = TemplateLoader(paths: [path])
let loader = FileSystemLoader(paths: [path])
$0.it("can inherit from another template") {
let context = Context(dictionary: ["loader": loader])
let template = loader.loadTemplate("child.html")
let template = try loader.loadTemplate(name: "child.html")
try expect(try template?.render(context)) == "Header\nChild"
}
$0.it("can inherit from another template inheriting from another template") {
let context = Context(dictionary: ["loader": loader])
let template = loader.loadTemplate("child-child.html")
let template = try loader.loadTemplate(name: "child-child.html")
try expect(try template?.render(context)) == "Child Child Header\nChild"
}
}

View File

@@ -6,18 +6,18 @@ import PathKit
func testTemplateLoader() {
describe("TemplateLoader") {
let path = Path(#file) + ".." + "fixtures"
let loader = TemplateLoader(paths: [path])
let loader = FileSystemLoader(paths: [path])
$0.it("returns nil when a template cannot be found") {
try expect(loader.loadTemplate("unknown.html")).to.beNil()
try expect(try loader.loadTemplate(name: "unknown.html")).to.beNil()
}
$0.it("returns nil when an array of templates cannot be found") {
try expect(loader.loadTemplate(["unknown.html", "unknown2.html"])).to.beNil()
try expect(try loader.loadTemplate(names: ["unknown.html", "unknown2.html"])).to.beNil()
}
$0.it("can load a template from a file") {
if loader.loadTemplate("test.html") == nil {
if try loader.loadTemplate(name: "test.html") == nil {
throw failure("didn't find the template")
}
}

View File

@@ -117,12 +117,12 @@ You can include another template using the `include` tag.
{% include "comment.html" %}
The `include` tag requires a TemplateLoader to be found inside your context with the paths, or bundles used to lookup the template.
The `include` tag requires a FileSystemLoader to be found inside your context with the paths, or bundles used to lookup the template.
.. code-block:: swift
let context = Context(dictionary: [
"loader": TemplateLoader(bundle:[NSBundle.mainBundle()])
"loader": FileSystemLoader(bundle: [NSBundle.mainBundle()])
])
``extends``