method -> transform

This commit is contained in:
Adam Fowler
2021-03-22 12:11:22 +00:00
parent 35d52603e2
commit c21651ce50
6 changed files with 53 additions and 52 deletions

View File

@@ -20,7 +20,7 @@ struct HBMustacheSequenceContext: HBMustacheTransformable {
/// ``` /// ```
/// ///
/// Transforms available are `first`, `last`, `index`, `even` and `odd` /// Transforms available are `first`, `last`, `index`, `even` and `odd`
/// - Parameter name: Method name /// - Parameter name: transform name
/// - Returns: Result /// - Returns: Result
func transform(_ name: String) -> Any? { func transform(_ name: String) -> Any? {
switch name { switch name {

View File

@@ -26,7 +26,7 @@ extension HBMustacheTemplate {
struct ParserState { struct ParserState {
var sectionName: String? var sectionName: String?
var sectionMethod: String? var sectionTransform: String?
var newLine: Bool var newLine: Bool
var startDelimiter: String var startDelimiter: String
var endDelimiter: String var endDelimiter: String
@@ -38,10 +38,10 @@ extension HBMustacheTemplate {
self.endDelimiter = "}}" self.endDelimiter = "}}"
} }
func withSectionName(_ name: String, method: String? = nil) -> ParserState { func withSectionName(_ name: String, transform: String? = nil) -> ParserState {
var newValue = self var newValue = self
newValue.sectionName = name newValue.sectionName = name
newValue.sectionMethod = method newValue.sectionTransform = transform
return newValue return newValue
} }
@@ -104,50 +104,50 @@ extension HBMustacheTemplate {
case "#": case "#":
// section // section
parser.unsafeAdvance() parser.unsafeAdvance()
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
if self.isStandalone(&parser, state: state) { if self.isStandalone(&parser, state: state) {
setNewLine = true setNewLine = true
} else if whiteSpaceBefore.count > 0 { } else if whiteSpaceBefore.count > 0 {
tokens.append(.text(String(whiteSpaceBefore))) tokens.append(.text(String(whiteSpaceBefore)))
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, method: method)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
tokens.append(.section(name: name, method: method, template: HBMustacheTemplate(sectionTokens))) tokens.append(.section(name: name, transform: transform, template: HBMustacheTemplate(sectionTokens)))
case "^": case "^":
// inverted section // inverted section
parser.unsafeAdvance() parser.unsafeAdvance()
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
if self.isStandalone(&parser, state: state) { if self.isStandalone(&parser, state: state) {
setNewLine = true setNewLine = true
} else if whiteSpaceBefore.count > 0 { } else if whiteSpaceBefore.count > 0 {
tokens.append(.text(String(whiteSpaceBefore))) tokens.append(.text(String(whiteSpaceBefore)))
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, method: method)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
tokens.append(.invertedSection(name: name, method: method, template: HBMustacheTemplate(sectionTokens))) tokens.append(.invertedSection(name: name, transform: transform, template: HBMustacheTemplate(sectionTokens)))
case "$": case "$":
// inherited section // inherited section
parser.unsafeAdvance() parser.unsafeAdvance()
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
// ERROR: can't have methods applied to inherited sections // ERROR: can't have transform applied to inherited sections
guard method == nil else { throw Error.transformAppliedToInheritanceSection } guard transform == nil else { throw Error.transformAppliedToInheritanceSection }
if self.isStandalone(&parser, state: state) { if self.isStandalone(&parser, state: state) {
setNewLine = true setNewLine = true
} else if whiteSpaceBefore.count > 0 { } else if whiteSpaceBefore.count > 0 {
tokens.append(.text(String(whiteSpaceBefore))) tokens.append(.text(String(whiteSpaceBefore)))
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, method: method)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
tokens.append(.inheritedSection(name: name, template: HBMustacheTemplate(sectionTokens))) tokens.append(.inheritedSection(name: name, template: HBMustacheTemplate(sectionTokens)))
case "/": case "/":
// end of section // end of section
parser.unsafeAdvance() parser.unsafeAdvance()
let position = parser.position let position = parser.position
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
guard name == state.sectionName, method == state.sectionMethod else { guard name == state.sectionName, transform == state.sectionTransform else {
parser.unsafeSetPosition(position) parser.unsafeSetPosition(position)
throw Error.sectionCloseNameIncorrect throw Error.sectionCloseNameIncorrect
} }
@@ -172,9 +172,9 @@ extension HBMustacheTemplate {
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
parser.unsafeAdvance() parser.unsafeAdvance()
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
guard try parser.read("}") else { throw Error.unfinishedName } guard try parser.read("}") else { throw Error.unfinishedName }
tokens.append(.unescapedVariable(name: name, method: method)) tokens.append(.unescapedVariable(name: name, transform: transform))
case "&": case "&":
// unescaped variable // unescaped variable
@@ -183,8 +183,8 @@ extension HBMustacheTemplate {
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
parser.unsafeAdvance() parser.unsafeAdvance()
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
tokens.append(.unescapedVariable(name: name, method: method)) tokens.append(.unescapedVariable(name: name, transform: transform))
case ">": case ">":
// partial // partial
@@ -204,9 +204,9 @@ extension HBMustacheTemplate {
case "<": case "<":
// partial with inheritance // partial with inheritance
parser.unsafeAdvance() parser.unsafeAdvance()
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
// ERROR: can't have methods applied to inherited sections // ERROR: can't have transform applied to inherited sections
guard method == nil else { throw Error.transformAppliedToInheritanceSection } guard transform == nil else { throw Error.transformAppliedToInheritanceSection }
var indent: String? var indent: String?
if self.isStandalone(&parser, state: state) { if self.isStandalone(&parser, state: state) {
setNewLine = true setNewLine = true
@@ -215,8 +215,9 @@ extension HBMustacheTemplate {
tokens.append(.text(indent!)) tokens.append(.text(indent!))
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, method: method)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
var inherit: [String: HBMustacheTemplate] = [:] var inherit: [String: HBMustacheTemplate] = [:]
// parse tokens in section to extract inherited sections
for token in sectionTokens { for token in sectionTokens {
switch token { switch token {
case .inheritedSection(let name, let template): case .inheritedSection(let name, let template):
@@ -241,8 +242,8 @@ extension HBMustacheTemplate {
tokens.append(.text(String(whiteSpaceBefore))) tokens.append(.text(String(whiteSpaceBefore)))
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let (name, method) = try parseName(&parser, state: state) let (name, transform) = try parseName(&parser, state: state)
tokens.append(.variable(name: name, method: method)) tokens.append(.variable(name: name, transform: transform))
} }
state.newLine = setNewLine state.newLine = setNewLine
} }
@@ -285,7 +286,7 @@ extension HBMustacheTemplate {
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
guard try parser.read(string: state.endDelimiter) else { throw Error.unfinishedName } guard try parser.read(string: state.endDelimiter) else { throw Error.unfinishedName }
// does the name include brackets. If so this is a method call // does the name include brackets. If so this is a transform call
var nameParser = HBParser(String(text)) var nameParser = HBParser(String(text))
let string = nameParser.read(while: self.sectionNameCharsWithoutBrackets) let string = nameParser.read(while: self.sectionNameCharsWithoutBrackets)
if nameParser.reachedEnd() { if nameParser.reachedEnd() {

View File

@@ -27,24 +27,24 @@ extension HBMustacheTemplate {
switch token { switch token {
case .text(let text): case .text(let text):
return text return text
case .variable(let variable, let method): case .variable(let variable, let transform):
if let child = getChild(named: variable, method: method, context: context) { if let child = getChild(named: variable, transform: transform, context: context) {
if let template = child as? HBMustacheTemplate { if let template = child as? HBMustacheTemplate {
return template.render(context: context) return template.render(context: context)
} else { } else {
return String(describing: child).htmlEscape() return String(describing: child).htmlEscape()
} }
} }
case .unescapedVariable(let variable, let method): case .unescapedVariable(let variable, let transform):
if let child = getChild(named: variable, method: method, context: context) { if let child = getChild(named: variable, transform: transform, context: context) {
return String(describing: child) return String(describing: child)
} }
case .section(let variable, let method, let template): case .section(let variable, let transform, let template):
let child = self.getChild(named: variable, method: method, context: context) let child = self.getChild(named: variable, transform: transform, context: context)
return self.renderSection(child, with: template, context: context) return self.renderSection(child, with: template, context: context)
case .invertedSection(let variable, let method, let template): case .invertedSection(let variable, let transform, let template):
let child = self.getChild(named: variable, method: method, context: context) let child = self.getChild(named: variable, transform: transform, context: context)
return self.renderInvertedSection(child, with: template, context: context) return self.renderInvertedSection(child, with: template, context: context)
case .inheritedSection(let name, let template): case .inheritedSection(let name, let template):
@@ -103,7 +103,7 @@ extension HBMustacheTemplate {
} }
/// Get child object from variable name /// Get child object from variable name
func getChild(named name: String, method: String?, context: HBMustacheContext) -> Any? { func getChild(named name: String, transform: String?, context: HBMustacheContext) -> Any? {
func _getImmediateChild(named name: String, from object: Any) -> Any? { func _getImmediateChild(named name: String, from object: Any) -> Any? {
if let customBox = object as? HBMustacheParent { if let customBox = object as? HBMustacheParent {
return customBox.child(named: name) return customBox.child(named: name)
@@ -132,23 +132,23 @@ extension HBMustacheTemplate {
} }
// work out which object to access. "." means the current object, if the variable name is "" // work out which object to access. "." means the current object, if the variable name is ""
// and we have a method to run on the variable then we need the context object, otherwise // and we have a transform to run on the variable then we need the context object, otherwise
// the name is split by "." and we use mirror to get the correct child object // the name is split by "." and we use mirror to get the correct child object
let child: Any? let child: Any?
if name == "." { if name == "." {
child = context.stack.last! child = context.stack.last!
} else if name == "", method != nil { } else if name == "", transform != nil {
child = context.sequenceContext child = context.sequenceContext
} else { } else {
let nameSplit = name.split(separator: ".").map { String($0) } let nameSplit = name.split(separator: ".").map { String($0) }
child = _getChildInStack(named: nameSplit[...], from: context.stack) child = _getChildInStack(named: nameSplit[...], from: context.stack)
} }
// if we want to run a method and the current child can have methods applied to it then // if we want to run a transform and the current child can have transforms applied to it then
// run method on the current child // run transform on the current child
if let method = method, if let transform = transform,
let runnable = child as? HBMustacheTransformable let runnable = child as? HBMustacheTransformable
{ {
if let result = runnable.transform(method) { if let result = runnable.transform(transform) {
return result return result
} }
} }

View File

@@ -34,10 +34,10 @@ public final class HBMustacheTemplate {
enum Token { enum Token {
case text(String) case text(String)
case variable(name: String, method: String? = nil) case variable(name: String, transform: String? = nil)
case unescapedVariable(name: String, method: String? = nil) case unescapedVariable(name: String, transform: String? = nil)
case section(name: String, method: String? = nil, template: HBMustacheTemplate) case section(name: String, transform: String? = nil, template: HBMustacheTemplate)
case invertedSection(name: String, method: String? = nil, template: HBMustacheTemplate) case invertedSection(name: String, transform: String? = nil, template: HBMustacheTemplate)
case inheritedSection(name: String, template: HBMustacheTemplate) case inheritedSection(name: String, template: HBMustacheTemplate)
case partial(String, indentation: String?, inherits: [String: HBMustacheTemplate]?) case partial(String, indentation: String?, inherits: [String: HBMustacheTemplate]?)
} }

View File

@@ -1,7 +1,7 @@
/// Objects that can have a transforms run on them. Mustache transforms are specific to this implementation /// Objects that can have a transforms run on them. Mustache transforms are specific to this implementation
/// of Mustache. They allow you to process objects before they are rendered. /// of Mustache. They allow you to process objects before they are rendered.
/// ///
/// The syntax for applying transforms is `{{method(variable)}}`. Transforms can be applied to both /// The syntax for applying transforms is `{{transform(variable)}}`. Transforms can be applied to both
/// variables, sections and inverted sections. /// variables, sections and inverted sections.
/// ///
/// A simple example would be ensuring a string is lowercase. /// A simple example would be ensuring a string is lowercase.
@@ -21,7 +21,7 @@ public extension StringProtocol {
/// Transform String/Substring /// Transform String/Substring
/// ///
/// Transforms available are `capitalized`, `lowercased`, `uppercased` and `reversed` /// Transforms available are `capitalized`, `lowercased`, `uppercased` and `reversed`
/// - Parameter name: Method name /// - Parameter name: transform name
/// - Returns: Result /// - Returns: Result
func transform(_ name: String) -> Any? { func transform(_ name: String) -> Any? {
switch name { switch name {
@@ -52,7 +52,7 @@ extension Array: HBMustacheTransformable {
/// ///
/// Transforms available are `first`, `last`, `reversed`, `count` and for arrays /// Transforms available are `first`, `last`, `reversed`, `count` and for arrays
/// with comparable elements `sorted`. /// with comparable elements `sorted`.
/// - Parameter name: method name /// - Parameter name: transform name
/// - Returns: Result /// - Returns: Result
public func transform(_ name: String) -> Any? { public func transform(_ name: String) -> Any? {
switch name { switch name {
@@ -89,7 +89,7 @@ extension Dictionary: HBMustacheTransformable {
/// ///
/// Transforms available are `count`, `enumerated` and for dictionaries /// Transforms available are `count`, `enumerated` and for dictionaries
/// with comparable keys `sorted`. /// with comparable keys `sorted`.
/// - Parameter name: method name /// - Parameter name: transform name
/// - Returns: Result /// - Returns: Result
public func transform(_ name: String) -> Any? { public func transform(_ name: String) -> Any? {
switch name { switch name {
@@ -121,7 +121,7 @@ public extension FixedWidthInteger {
/// Transform FixedWidthInteger /// Transform FixedWidthInteger
/// ///
/// Transforms available are `plusone`, `minusone`, `odd`, `even` /// Transforms available are `plusone`, `minusone`, `odd`, `even`
/// - Parameter name: method name /// - Parameter name: transform name
/// - Returns: Result /// - Returns: Result
func transform(_ name: String) -> Any? { func transform(_ name: String) -> Any? {
switch name { switch name {

View File

@@ -1,7 +1,7 @@
import HummingbirdMustache import HummingbirdMustache
import XCTest import XCTest
final class MethodTests: XCTestCase { final class TransformTests: XCTestCase {
func testLowercased() throws { func testLowercased() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{ lowercased(name) }} {{ lowercased(name) }}