Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
Merge pull request #6 from g-Off/filesystem
Browse files Browse the repository at this point in the history
adding the concept of a filesystem to the template
  • Loading branch information
g-Off authored Oct 1, 2019
2 parents 491ba0d + 62055ab commit 9c35230
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 15 deletions.
8 changes: 6 additions & 2 deletions Sources/Liquid/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,21 @@ public final class Context {
}

private var scopes: [Scope]
private var filters: [String: FilterFunc]

// Registers are for internal data structure storage, like forloop's and cycles to store data
private var registers: [String: Any] = [:]

public let filters: [String: FilterFunc]
public let tags: [String: TagBuilder]
public let environment: Environment
public let encoder: Encoder
public let fileSystem: FileSystem

init(values: [String: Value] = [:], environment: Environment = Environment(), filters: [String: FilterFunc] = [:], encoder: Encoder) {
init(fileSystem: FileSystem, values: [String: Value] = [:], environment: Environment = Environment(), tags: [String: TagBuilder], filters: [String: FilterFunc] = [:], encoder: Encoder) {
self.fileSystem = fileSystem
self.scopes = [Scope(values: values)]
self.environment = environment
self.tags = tags
self.filters = filters
self.encoder = encoder
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// SyntaxError.swift
// Error.swift
//
//
// Created by Geoffrey Foster on 2019-09-02.
Expand All @@ -18,9 +18,8 @@ public enum SyntaxError: Error {
public enum RuntimeError: Error {
case wrongType(String)
case unknownFilter(String)

case invalidArgCount(expected: Int, received: Int, tag: String)

case reason(String) // TODO: this is just a raw string explaining the failure
case unimplemented
}

Expand Down
43 changes: 35 additions & 8 deletions Sources/Liquid/Template.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,43 @@ public final class Template {
private var filters: [String: FilterFunc] = [:]
private var tags: [String: TagBuilder] = [:]

private let locale: Locale

var encoder: Encoder = Encoder()

/// Environments are persisted across each render of a template so we store them as part of the template itself.
/// The template then injects them during a render call and copies the values back upon completion.
private let environment: Environment

public init(source: String, locale: Locale = .current, environment: Environment = Environment()) {
private let fileSystem: FileSystem

public convenience init(sourceURL: URL, encoder: Encoder = Encoder(), environment: Environment = Environment()) throws {
let source = try String(contentsOf: sourceURL)
let fileSystem = LocalFileSystem(baseURL: sourceURL.deletingLastPathComponent())
self.init(source: source, fileSystem: fileSystem, encoder: encoder, environment: environment)
}

public convenience init(source: String, encoder: Encoder = Encoder(), environment: Environment = Environment(), fileSystem: FileSystem? = nil) {
struct ThrowingFileSystem: FileSystem {
func read(path: String) throws -> String {
throw RuntimeError.reason("Invalid filesystem")
}
}
self.init(source: source, fileSystem: fileSystem ?? ThrowingFileSystem(), encoder: encoder, environment: environment)
}

init(source: String, context: Context) {
self.source = source
self.encoder = context.encoder
self.environment = context.environment
self.fileSystem = context.fileSystem
self.filters = context.filters
self.tags = context.tags
}

private init(source: String, fileSystem: FileSystem, encoder: Encoder, environment: Environment) {
self.source = source
self.locale = locale
self.fileSystem = fileSystem
self.environment = environment

encoder.locale = locale
self.encoder = encoder

tags["assign"] = Assign.init
tags["break"] = Break.init
Expand Down Expand Up @@ -59,11 +82,15 @@ public final class Template {
tags[name] = tag
}

public func render(values: [String: Value] = [:]) throws -> String {
let context = Context(values: values, environment: environment, filters: filters, encoder: encoder)
public func render(context: Context) throws -> String {
return try root.render(context: context).joined()
}

public func render(values: [String: Value] = [:]) throws -> String {
let context = Context(fileSystem: fileSystem, values: values, environment: environment, tags: tags, filters: filters, encoder: encoder)
return try render(context: context)
}

public func render(values: [String: ValueConvertible]) throws -> String {
return try self.render(values: values.mapValues { try encoder.encode($0) })
}
Expand Down
46 changes: 46 additions & 0 deletions Sources/Liquid/Utilities/FileSystem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// FileSystem.swift
//
//
// Created by Geoffrey Foster on 2019-09-28.
//

import Foundation

public protocol FileSystem {
func read(path: String) throws -> String
}

struct LocalFileSystem: FileSystem {
let baseURL: URL

init(baseURL: URL) {
self.baseURL = baseURL
}

func read(path: String) throws -> String {
let fileURL = try templateURL(for: path)
return try String(contentsOf: fileURL)
}

private func templateURL(for path: String) throws -> URL {
var components = path.components(separatedBy: "/")
guard !components.isEmpty else {
throw FileSystemError(reason: "")
}
let filename = "_\(components.popLast()!).liquid"
components.append(filename)
let transformedPath = components.joined(separator: "/")
return baseURL.appendingPathComponent(transformedPath)
}
}

struct BlankFileSystem: FileSystem {
func read(path: String) throws -> String {
throw FileSystemError(reason: "This liquid context does not allow includes.")
}
}

struct FileSystemError: Error {
let reason: String
}
4 changes: 4 additions & 0 deletions Sources/Liquid/Value/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ public struct Encoder {
public var decimalEncodingStrategy: DecimalEncodingStrategy = .scaled(8)
public internal(set) var locale: Locale = .current

public init() {

}

public func encode(_ input: Any?) throws -> Value {
guard let input = input else {
return Value()
Expand Down
4 changes: 2 additions & 2 deletions Tests/LiquidTests/XCTestCase+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import XCTest
@testable import Liquid

func XCTAssertTemplate(_ templateString: String, _ expression2: String, _ values: [String: Any?] = [:], _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
let template = Template(source: templateString)
func XCTAssertTemplate(_ templateString: String, _ expression2: String, _ values: [String: Any?] = [:], fileSystem: FileSystem = BlankFileSystem(), _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
let template = Template(source: templateString, fileSystem: fileSystem)
XCTAssertNoThrow(try template.parse())
do {
let result = try template.render(values: values)
Expand Down

0 comments on commit 9c35230

Please sign in to comment.