From 9e2a061795dd2a0fa99aa432b0b218fb006ed1bf Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Thu, 1 Dec 2016 00:17:04 +0000 Subject: [PATCH] refactor: Introducing Environments --- CHANGELOG.md | 28 +++++++++++++++ README.md | 17 +++++---- Sources/Context.swift | 8 +++-- Sources/Environment.swift | 36 +++++++++++++++++++ Sources/Errors.swift | 11 ++++-- Sources/Include.swift | 11 +++--- Sources/Inheritence.swift | 6 +--- Sources/Loader.swift | 16 ++++----- Sources/Template.swift | 28 +++++++++++---- Tests/StencilTests/ContextSpec.swift | 2 +- Tests/StencilTests/EnvironmentSpec.swift | 41 +++++++++++++++++++++ Tests/StencilTests/FilterSpec.swift | 2 +- Tests/StencilTests/IncludeSpec.swift | 7 ++-- Tests/StencilTests/InheritenceSpec.swift | 11 +++--- Tests/StencilTests/LoaderSpec.swift | 11 +++--- Tests/StencilTests/NodeSpec.swift | 2 +- Tests/StencilTests/ParserSpec.swift | 2 +- Tests/StencilTests/StencilSpec.swift | 19 +++++----- Tests/StencilTests/TemplateSpec.swift | 6 ++-- Tests/StencilTests/VariableSpec.swift | 2 +- Tests/StencilTests/XCTest.swift | 1 + docs/api/context.rst | 6 ---- docs/api/environment.rst | 43 +++++++++++++++++++++++ docs/api/loader.rst | 38 ++++++++++++++++++++ docs/builtins.rst | 8 ++--- docs/custom-template-tags-and-filters.rst | 4 ++- docs/index.rst | 14 ++++---- 27 files changed, 289 insertions(+), 91 deletions(-) create mode 100644 Sources/Environment.swift create mode 100644 Tests/StencilTests/EnvironmentSpec.swift create mode 100644 docs/api/environment.rst create mode 100644 docs/api/loader.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 003412c..1a968fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,39 @@ ### Breaking +- It is no longer possible to create `Context` objects. Instead, you can pass a + dictionary directly to a `Template`s `render` method. + + ```diff + - try template.render(Context(dictionary: ["name": "Kyle"])) + + try template.render(["name": "Kyle"]) + ``` + +- Template loader are no longer passed into a `Context`, instead you will need + to pass the `Loader` to an `Environment` and create a template from the + `Environment`. + + ```diff + let loader = FileSystemLoader(paths: ["templates/"]) + + - let template = loader.loadTemplate(name: "index.html") + - try template.render(Context(dictionary: ["loader": loader])) + + + let environment = Environment(loader: loader) + + try environment.renderTemplate(name: "index.html") + ``` + - `Loader`s will now throw a `TemplateDoesNotExist` error when a template is not found. ### Enhancements +- `Environment` is a new way to load templates. You can configure an + environment with custom template filters, tags and loaders and then create a + template from an environment. + + Environment also provides a convenience method to render a template directly. + - `FileSystemLoader` will now ensure that template paths are within the base path. Any template names that try to escape the base path will raise a `SuspiciousFileOperation` error. diff --git a/README.md b/README.md index 5bbe1a2..9426a7a 100644 --- a/README.md +++ b/README.md @@ -19,25 +19,24 @@ There are {{ articles.count }} articles. ``` ```swift +import Stencil + struct Article { let title: String let author: String } -let context = Context(dictionary: [ +let context = [ "articles": [ Article(title: "Migrating from OCUnit to XCTest", author: "Kyle Fuller"), Article(title: "Memory Management with ARC", author: "Kyle Fuller"), ] -]) +] -do { - let template = try Template(named: "template.html") - let rendered = try template.render(context) - print(rendered) -} catch { - print("Failed to render template \(error)") -} +let environment = Environment(loader: FileSystemLoader(paths: ["templates/"]) +let rendered = try environment.renderTemplate(name: context) + +print(rendered) ``` ## Installation diff --git a/Sources/Context.swift b/Sources/Context.swift index a75cfc9..2789ce5 100644 --- a/Sources/Context.swift +++ b/Sources/Context.swift @@ -1,17 +1,19 @@ /// A container for template variables. public class Context { var dictionaries: [[String: Any?]] + + public let environment: Environment let namespace: Namespace - /// Initialise a Context with an optional dictionary and optional namespace - public init(dictionary: [String: Any]? = nil, namespace: Namespace = Namespace()) { + init(dictionary: [String: Any]? = nil, namespace: Namespace? = nil, environment: Environment? = nil) { if let dictionary = dictionary { dictionaries = [dictionary] } else { dictionaries = [] } - self.namespace = namespace + self.namespace = namespace ?? environment?.namespace ?? Namespace() + self.environment = environment ?? Environment() } public subscript(key: String) -> Any? { diff --git a/Sources/Environment.swift b/Sources/Environment.swift new file mode 100644 index 0000000..b89fe1f --- /dev/null +++ b/Sources/Environment.swift @@ -0,0 +1,36 @@ +public struct Environment { + var namespace: Namespace + + public var loader: Loader? + + public init(loader: Loader? = nil, namespace: Namespace? = nil) { + self.loader = loader + self.namespace = namespace ?? Namespace() + } + + public func loadTemplate(name: String) throws -> Template { + if let loader = loader { + return try loader.loadTemplate(name: name, environment: self) + } else { + throw TemplateDoesNotExist(templateNames: [name], loader: nil) + } + } + + public func loadTemplate(names: [String]) throws -> Template { + if let loader = loader { + return try loader.loadTemplate(names: names, environment: self) + } else { + throw TemplateDoesNotExist(templateNames: names, loader: nil) + } + } + + public func renderTemplate(name: String, context: [String: Any]? = nil) throws -> String { + let template = try loadTemplate(name: name) + return try template.render(context) + } + + public func renderTemplate(string: String, context: [String: Any]? = nil) throws -> String { + let template = Template(templateString: string, environment: self) + return try template.render(context) + } +} diff --git a/Sources/Errors.swift b/Sources/Errors.swift index 7151561..964eeae 100644 --- a/Sources/Errors.swift +++ b/Sources/Errors.swift @@ -1,14 +1,19 @@ public class TemplateDoesNotExist: Error, CustomStringConvertible { let templateNames: [String] - let loader: Loader + let loader: Loader? - public init(templateNames: [String], loader: Loader) { + public init(templateNames: [String], loader: Loader? = nil) { self.templateNames = templateNames self.loader = loader } public var description: String { let templates = templateNames.joined(separator: ", ") - return "Template named `\(templates)` does not exist in loader \(loader)" + + if let loader = loader { + return "Template named `\(templates)` does not exist in loader \(loader)" + } + + return "Template named `\(templates)` does not exist. No loaders found" } } diff --git a/Sources/Include.swift b/Sources/Include.swift index aac580d..cd9cc5c 100644 --- a/Sources/Include.swift +++ b/Sources/Include.swift @@ -19,16 +19,15 @@ class IncludeNode : NodeType { } func render(_ context: Context) throws -> String { - guard let loader = context["loader"] as? Loader else { - throw TemplateSyntaxError("Template loader not in context") - } - guard let templateName = try self.templateName.resolve(context) as? String else { throw TemplateSyntaxError("'\(self.templateName)' could not be resolved as a string") } - let template = try loader.loadTemplate(name: templateName) - return try template.render(context) + let template = try context.environment.loadTemplate(name: templateName) + + return try context.push { + return try template.render(context) + } } } diff --git a/Sources/Inheritence.swift b/Sources/Inheritence.swift index 941d626..8323d00 100644 --- a/Sources/Inheritence.swift +++ b/Sources/Inheritence.swift @@ -59,15 +59,11 @@ class ExtendsNode : NodeType { } func render(_ context: Context) throws -> String { - guard let loader = context["loader"] as? Loader else { - throw TemplateSyntaxError("Template loader not in context") - } - guard let templateName = try self.templateName.resolve(context) as? String else { throw TemplateSyntaxError("'\(self.templateName)' could not be resolved as a string") } - let template = try loader.loadTemplate(name: templateName) + let template = try context.environment.loadTemplate(name: templateName) let blockContext: BlockContext if let context = context[BlockContext.contextKey] as? BlockContext { diff --git a/Sources/Loader.swift b/Sources/Loader.swift index f44cb04..fd49ece 100644 --- a/Sources/Loader.swift +++ b/Sources/Loader.swift @@ -3,16 +3,16 @@ import PathKit public protocol Loader { - func loadTemplate(name: String) throws -> Template - func loadTemplate(names: [String]) throws -> Template + func loadTemplate(name: String, environment: Environment) throws -> Template + func loadTemplate(names: [String], environment: Environment) throws -> Template } extension Loader { - func loadTemplate(names: [String]) throws -> Template { + public func loadTemplate(names: [String], environment: Environment) throws -> Template { for name in names { do { - return try loadTemplate(name: name) + return try loadTemplate(name: name, environment: environment) } catch is TemplateDoesNotExist { continue } catch { @@ -43,7 +43,7 @@ public class FileSystemLoader: Loader, CustomStringConvertible { return "FileSystemLoader(\(paths))" } - public func loadTemplate(name: String) throws -> Template { + public func loadTemplate(name: String, environment: Environment) throws -> Template { for path in paths { let templatePath = try path.safeJoin(path: Path(name)) @@ -51,19 +51,19 @@ public class FileSystemLoader: Loader, CustomStringConvertible { continue } - return try Template(path: templatePath) + return try Template(path: templatePath, environment: environment, name: name) } throw TemplateDoesNotExist(templateNames: [name], loader: self) } - public func loadTemplate(names: [String]) throws -> Template { + public func loadTemplate(names: [String], environment: Environment) throws -> Template { for path in paths { for templateName in names { let templatePath = try path.safeJoin(path: Path(templateName)) if templatePath.exists { - return try Template(path: templatePath) + return try Template(path: templatePath, environment: environment, name: templateName) } } } diff --git a/Sources/Template.swift b/Sources/Template.swift index 305b32b..15a955b 100644 --- a/Sources/Template.swift +++ b/Sources/Template.swift @@ -7,10 +7,17 @@ let NSFileNoSuchFileError = 4 /// A class representing a template public class Template: ExpressibleByStringLiteral { + let environment: Environment let tokens: [Token] + /// The name of the loaded Template if the Template was loaded from a Loader + public let name: String? + /// Create a template with a template string - public init(templateString: String) { + public init(templateString: String, environment: Environment? = nil, name: String? = nil) { + self.environment = environment ?? Environment() + self.name = name + let lexer = Lexer(templateString: templateString) tokens = lexer.tokenize() } @@ -31,11 +38,13 @@ public class Template: ExpressibleByStringLiteral { } /// Create a template with a file found at the given path - public convenience init(path: Path) throws { - self.init(templateString: try path.read()) + public convenience init(path: Path, environment: Environment? = nil, name: String? = nil) throws { + self.init(templateString: try path.read(), environment: environment, name: name) } - // Create a template with a template string literal + // MARK: ExpressibleByStringLiteral + + // Create a templaVte with a template string literal public convenience required init(stringLiteral value: String) { self.init(templateString: value) } @@ -50,11 +59,16 @@ public class Template: ExpressibleByStringLiteral { self.init(stringLiteral: value) } - /// Render the given template - public func render(_ context: Context? = nil) throws -> String { - let context = context ?? Context() + /// Render the given template with a context + func render(_ context: Context) throws -> String { + let context = context ?? Context(environment: environment) let parser = TokenParser(tokens: tokens, namespace: context.namespace) let nodes = try parser.parse() return try renderNodes(nodes, context) } + + /// Render the given template + public func render(_ dictionary: [String: Any]? = nil) throws -> String { + return try render(Context(dictionary: dictionary, environment: environment)) + } } diff --git a/Tests/StencilTests/ContextSpec.swift b/Tests/StencilTests/ContextSpec.swift index d90c29c..d33e1d8 100644 --- a/Tests/StencilTests/ContextSpec.swift +++ b/Tests/StencilTests/ContextSpec.swift @@ -1,5 +1,5 @@ import Spectre -import Stencil +@testable import Stencil func testContext() { diff --git a/Tests/StencilTests/EnvironmentSpec.swift b/Tests/StencilTests/EnvironmentSpec.swift new file mode 100644 index 0000000..1c1e942 --- /dev/null +++ b/Tests/StencilTests/EnvironmentSpec.swift @@ -0,0 +1,41 @@ +import Spectre +import Stencil + + +func testEnvironment() { + describe("Environment") { + let environment = Environment(loader: ExampleLoader()) + + $0.it("can load a template from a name") { + let template = try environment.loadTemplate(name: "example.html") + try expect(template.name) == "example.html" + } + + $0.it("can load a template from a names") { + let template = try environment.loadTemplate(names: ["first.html", "example.html"]) + try expect(template.name) == "example.html" + } + + $0.it("can render a template from a string") { + let result = try environment.renderTemplate(string: "Hello World") + try expect(result) == "Hello World" + } + + $0.it("can render a template from a file") { + let result = try environment.renderTemplate(name: "example.html") + try expect(result) == "Hello World!" + } + } +} + + + +fileprivate class ExampleLoader: Loader { + func loadTemplate(name: String, environment: Environment) throws -> Template { + if name == "example.html" { + return Template(templateString: "Hello World!", environment: environment, name: name) + } + + throw TemplateDoesNotExist(templateNames: [name], loader: self) + } +} diff --git a/Tests/StencilTests/FilterSpec.swift b/Tests/StencilTests/FilterSpec.swift index 5b63057..78d45a2 100644 --- a/Tests/StencilTests/FilterSpec.swift +++ b/Tests/StencilTests/FilterSpec.swift @@ -1,5 +1,5 @@ import Spectre -import Stencil +@testable import Stencil func testFilter() { diff --git a/Tests/StencilTests/IncludeSpec.swift b/Tests/StencilTests/IncludeSpec.swift index 85ab839..b0f687b 100644 --- a/Tests/StencilTests/IncludeSpec.swift +++ b/Tests/StencilTests/IncludeSpec.swift @@ -7,6 +7,7 @@ func testInclude() { describe("Include") { let path = Path(#file) + ".." + "fixtures" let loader = FileSystemLoader(paths: [path]) + let environment = Environment(loader: loader) $0.describe("parsing") { $0.it("throws an error when no template is given") { @@ -35,7 +36,7 @@ func testInclude() { do { _ = try node.render(Context()) } catch { - try expect("\(error)") == "Template loader not in context" + try expect("\(error)") == "Template named `test.html` does not exist. No loaders found" } } @@ -43,7 +44,7 @@ func testInclude() { let node = IncludeNode(templateName: Variable("\"unknown.html\"")) do { - _ = try node.render(Context(dictionary: ["loader": loader])) + _ = try node.render(Context(environment: environment)) } catch { try expect("\(error)".hasPrefix("Template named `unknown.html` does not exist in loader")).to.beTrue() } @@ -51,7 +52,7 @@ func testInclude() { $0.it("successfully renders a found included template") { let node = IncludeNode(templateName: Variable("\"test.html\"")) - let context = Context(dictionary: ["loader":loader, "target": "World"]) + let context = Context(dictionary: ["target": "World"], environment: environment) let value = try node.render(context) try expect(value) == "Hello World!" } diff --git a/Tests/StencilTests/InheritenceSpec.swift b/Tests/StencilTests/InheritenceSpec.swift index 88c9a17..43ed59f 100644 --- a/Tests/StencilTests/InheritenceSpec.swift +++ b/Tests/StencilTests/InheritenceSpec.swift @@ -7,17 +7,16 @@ func testInheritence() { describe("Inheritence") { let path = Path(#file) + ".." + "fixtures" let loader = FileSystemLoader(paths: [path]) + let environment = Environment(loader: loader) $0.it("can inherit from another template") { - let context = Context(dictionary: ["loader": loader]) - let template = try loader.loadTemplate(name: "child.html") - try expect(try template.render(context)) == "Header\nChild" + let template = try environment.loadTemplate(name: "child.html") + try expect(try template.render()) == "Header\nChild" } $0.it("can inherit from another template inheriting from another template") { - let context = Context(dictionary: ["loader": loader]) - let template = try loader.loadTemplate(name: "child-child.html") - try expect(try template.render(context)) == "Child Child Header\nChild" + let template = try environment.loadTemplate(name: "child-child.html") + try expect(try template.render()) == "Child Child Header\nChild" } } } diff --git a/Tests/StencilTests/LoaderSpec.swift b/Tests/StencilTests/LoaderSpec.swift index 6999613..e4d6588 100644 --- a/Tests/StencilTests/LoaderSpec.swift +++ b/Tests/StencilTests/LoaderSpec.swift @@ -7,25 +7,26 @@ func testTemplateLoader() { describe("FileSystemLoader") { let path = Path(#file) + ".." + "fixtures" let loader = FileSystemLoader(paths: [path]) + let environment = Environment(loader: loader) $0.it("errors when a template cannot be found") { - try expect(try loader.loadTemplate(name: "unknown.html")).toThrow() + try expect(try environment.loadTemplate(name: "unknown.html")).toThrow() } $0.it("errors when an array of templates cannot be found") { - try expect(try loader.loadTemplate(names: ["unknown.html", "unknown2.html"])).toThrow() + try expect(try environment.loadTemplate(names: ["unknown.html", "unknown2.html"])).toThrow() } $0.it("can load a template from a file") { - _ = try loader.loadTemplate(name: "test.html") + _ = try environment.loadTemplate(name: "test.html") } $0.it("errors when loading absolute file outside of the selected path") { - try expect(try loader.loadTemplate(name: "/etc/hosts")).toThrow() + try expect(try environment.loadTemplate(name: "/etc/hosts")).toThrow() } $0.it("errors when loading relative file outside of the selected path") { - try expect(try loader.loadTemplate(name: "../LoaderSpec.swift")).toThrow() + try expect(try environment.loadTemplate(name: "../LoaderSpec.swift")).toThrow() } } } diff --git a/Tests/StencilTests/NodeSpec.swift b/Tests/StencilTests/NodeSpec.swift index 5406c3d..431d225 100644 --- a/Tests/StencilTests/NodeSpec.swift +++ b/Tests/StencilTests/NodeSpec.swift @@ -1,5 +1,5 @@ import Spectre -import Stencil +@testable import Stencil class ErrorNode : NodeType { diff --git a/Tests/StencilTests/ParserSpec.swift b/Tests/StencilTests/ParserSpec.swift index 09e3918..8a46248 100644 --- a/Tests/StencilTests/ParserSpec.swift +++ b/Tests/StencilTests/ParserSpec.swift @@ -1,5 +1,5 @@ import Spectre -import Stencil +@testable import Stencil func testTokenParser() { diff --git a/Tests/StencilTests/StencilSpec.swift b/Tests/StencilTests/StencilSpec.swift index fa0cff2..43e908f 100644 --- a/Tests/StencilTests/StencilSpec.swift +++ b/Tests/StencilTests/StencilSpec.swift @@ -25,12 +25,12 @@ func testStencil() { " - {{ article.title }} by {{ article.author }}.\n" + "{% endfor %}\n" - let context = Context(dictionary: [ + let context = [ "articles": [ Article(title: "Migrating from OCUnit to XCTest", author: "Kyle Fuller"), Article(title: "Memory Management with ARC", author: "Kyle Fuller"), ] - ]) + ] let template = Template(templateString: templateString) let result = try template.render(context) @@ -45,28 +45,27 @@ func testStencil() { } $0.it("can render a custom template tag") { - let templateString = "{% custom %}" - let template = Template(templateString: templateString) - let namespace = Namespace() namespace.registerTag("custom") { parser, token in return CustomNode() } - let result = try template.render(Context(namespace: namespace)) + let environment = Environment(namespace: namespace) + let result = try environment.renderTemplate(string: "{% custom %}") + try expect(result) == "Hello World" } $0.it("can render a simple custom tag") { - let templateString = "{% custom %}" - let template = Template(templateString: templateString) - let namespace = Namespace() namespace.registerSimpleTag("custom") { context in return "Hello World" } - try expect(try template.render(Context(namespace: namespace))) == "Hello World" + let environment = Environment(namespace: namespace) + let result = try environment.renderTemplate(string: "{% custom %}") + + try expect(result) == "Hello World" } } } diff --git a/Tests/StencilTests/TemplateSpec.swift b/Tests/StencilTests/TemplateSpec.swift index cba7b17..ad03851 100644 --- a/Tests/StencilTests/TemplateSpec.swift +++ b/Tests/StencilTests/TemplateSpec.swift @@ -5,16 +5,14 @@ import Stencil func testTemplate() { describe("Template") { $0.it("can render a template from a string") { - let context = Context(dictionary: [ "name": "Kyle" ]) let template = Template(templateString: "Hello World") - let result = try template.render(context) + let result = try template.render([ "name": "Kyle" ]) try expect(result) == "Hello World" } $0.it("can render a template from a string literal") { - let context = Context(dictionary: [ "name": "Kyle" ]) let template: Template = "Hello World" - let result = try template.render(context) + let result = try template.render([ "name": "Kyle" ]) try expect(result) == "Hello World" } } diff --git a/Tests/StencilTests/VariableSpec.swift b/Tests/StencilTests/VariableSpec.swift index 19123d6..9543306 100644 --- a/Tests/StencilTests/VariableSpec.swift +++ b/Tests/StencilTests/VariableSpec.swift @@ -1,6 +1,6 @@ import Foundation import Spectre -import Stencil +@testable import Stencil #if os(OSX) diff --git a/Tests/StencilTests/XCTest.swift b/Tests/StencilTests/XCTest.swift index 8e875a0..b2d4de9 100644 --- a/Tests/StencilTests/XCTest.swift +++ b/Tests/StencilTests/XCTest.swift @@ -18,6 +18,7 @@ public func stencilTests() { testInclude() testInheritence() testFilterTag() + testEnvironment() testStencil() } diff --git a/docs/api/context.rst b/docs/api/context.rst index 113cefb..85d9a06 100644 --- a/docs/api/context.rst +++ b/docs/api/context.rst @@ -6,12 +6,6 @@ template. It’s somewhat like a dictionary, however you can push and pop to scope variables. So that means that when iterating over a for loop, you can push a new scope into the context to store any variables local to the scope. -You can initialise a ``Context`` with a ``Dictionary``. - -.. code-block:: swift - - Context(dictionary: [String: Any]? = nil) - API ---- diff --git a/docs/api/environment.rst b/docs/api/environment.rst new file mode 100644 index 0000000..190e3fe --- /dev/null +++ b/docs/api/environment.rst @@ -0,0 +1,43 @@ +Environment +=========== + +An environment contains shared configuration such as custom filters and tags +along with template loaders. + +.. code-block:: swift + + let environment = Environment() + +You can optionally provide a loader or namespace when creating an environment: + +.. code-block:: swift + + let environment = Environment(loader: ..., namespace: ...) + +Rendering a Template +-------------------- + +Environment providences coninience methods to render a template either from a +string or a template loader. + +.. code-block:: swift + + let template = "Hello {{ name }}" + let context = ["name": "Kyle"] + let rendered = environment.render(templateString: template, context: context) + +Rendering a template from the configured loader: + +.. code-block:: swift + + let context = ["name": "Kyle"] + let rendered = environment.render(templateName: "example.html", context: context) + +Loading a Template +------------------ + +Environment provides an API to load a template from the configured loader. + +.. code-block:: swift + + let template = try environment.loadTemplate(name: "example.html") diff --git a/docs/api/loader.rst b/docs/api/loader.rst new file mode 100644 index 0000000..945372b --- /dev/null +++ b/docs/api/loader.rst @@ -0,0 +1,38 @@ +Loader +====== + +Loaders are responsible for loading templates from a resource such as the file +system. + +Stencil provides a ``FileSytemLoader`` which allows you to load a template +directly from the file system. + +``Loader`` is a protocol, so you can implement your own compatible loaders. You +will need to implement a ``loadTemplate`` method to load the template, +throwing a ``TemplateDoesNotExist`` when the template is not found. + +.. code-block:: swift + + class ExampleMemoryLoader: Loader { + func loadTemplate(name: String, environment: Environment) throws -> Template { + if name == "index.html" { + return Template(templateString: "Hello", environment: environment) + } + + throw TemplateDoesNotExist() + } + } + +FileSystemLoader +---------------- + +Loads templates from the file system. This loader can find templates in folders +on the file system. + +.. code-block:: swift + + FileSystemLoader(paths: ["./templates"]) + +.. code-block:: swift + + FileSystemLoader(bundle: [Bundle.main]) diff --git a/docs/builtins.rst b/docs/builtins.rst index 33f8d92..90af0af 100644 --- a/docs/builtins.rst +++ b/docs/builtins.rst @@ -206,13 +206,13 @@ You can include another template using the `include` tag. {% include "comment.html" %} -The `include` tag requires a FileSystemLoader to be found inside your context with the paths, or bundles used to lookup the template. +The `include` tag requires you to provide a loader which will be used to lookup +the template. .. code-block:: swift - let context = Context(dictionary: [ - "loader": FileSystemLoader(bundle: [NSBundle.mainBundle()]) - ]) + let environment = Environment(bundle: [Bundle.main]) + let template = environment.loadTemplate(name: "index.html") ``extends`` ~~~~~~~~~~~ diff --git a/docs/custom-template-tags-and-filters.rst b/docs/custom-template-tags-and-filters.rst index c34f343..216313a 100644 --- a/docs/custom-template-tags-and-filters.rst +++ b/docs/custom-template-tags-and-filters.rst @@ -9,7 +9,9 @@ namespace which contains all filters and tags available to the template. let namespace = Namespace() // Register your filters and tags with the namespace - let rendered = try template.render(context, namespace: namespace) + + let environment = Environment(namespace: namespace) + try environment.renderTemplate(name: "example.html") Custom Filters -------------- diff --git a/docs/index.rst b/docs/index.rst index 10fbb37..4afac4c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,17 +17,19 @@ feel right at home with Stencil. .. code-block:: swift - struct Article { - let title: String - let author: String - } + import Stencil - let context = Context(dictionary: [ + struct Article { + let title: String + let author: String + } + + let context = [ "articles": [ Article(title: "Migrating from OCUnit to XCTest", author: "Kyle Fuller"), Article(title: "Memory Management with ARC", author: "Kyle Fuller"), ] - ]) + ] do { let template = try Template(named: "template.html")