外观
mustacheExt 扩展参考
原文: https://finproducts.atlassian.net/wiki/spaces/JIDD3/pages/34793554547/FIN+5.3.1+mustacheExt 中文整理基于 FIN 5.3.1
导读: Mustache 模板系统
以下为 API 参考,保留英文以便对照源码;目录已列出(英文标题便于检索);概述已尽量中译。
目录(导读)
- classes
- Slot Details
- call
- make
- wrappedFunc
- addTemplate
- context
- getReader
- run
- createMustacheVisitor
- resolvePartialPath
- mustache
- find
- get
- iterate
- allTags
- markerTags
- valueTags
- wrap
- wrapAny
- wrapGrid
- wrapList
- templatize
- fromDict
- fromMap
- name
- type
- value
- applyTemplate
- parseTemplate
概述(中译)
src 类 FantomWrapper : Obj, Wrapper
构造函数
字段
方法
插槽详情
src 虚拟重写 Obj? call(List? scopes)
API 正文(英文)
make
src new make(|List?->Obj?| f)
wrappedFunc
src |List?->Obj?| wrappedFunc
FinstackMustacheResolver
src class FinstackMustacheResolver : Obj, MustacheResolver
Constructors
| make |
Fields
| context |
Methods
| addTemplate | |
| getReader |
Slot Details
addTemplate
src Void addTemplate(Str name, Str template)
context
src HxContext context
getReader
src virtual override Reader? getReader(Str? resourceName)
make
src new make(HxContext context)
HaystackMustache
src class HaystackMustache : DefaultMustache
Constructors
| make |
Methods
| run |
Slot Details
make
src new make(TemplateContext cx, MustacheFactory factory, Code[] codes, Str name)
run
src virtual override Writer? run(Writer? writer, List? scopes)
HaystackMustacheFactory
src class HaystackMustacheFactory : DefaultMustacheFactory
Constructors
| make |
Methods
| createMustacheVisitor | |
| resolvePartialPath |
Slot Details
createMustacheVisitor
src virtual override MustacheVisitor? createMustacheVisitor()
make
src new make(HxContext context, FinstackMustacheResolver resolver)
resolvePartialPath
src virtual override Str? resolvePartialPath(Str? dir, Str? name, Str? extension)
HaystackMustacheVisitor
src class HaystackMustacheVisitor : DefaultMustacheVisitor
Constructors
| make |
Methods
| mustache |
Slot Details
make
src new make(MustacheFactory factory)
mustache
src virtual override Mustache? mustache(TemplateContext? cx)
HaystackObjectHandler
src class HaystackObjectHandler : SimpleObjectHandler
Constructors
| make |
Methods
| find | |
| get | |
| iterate |
Slot Details
find
src virtual override Wrapper? find(Str? name, List? scopes)
get
src virtual override Obj? get(Str? name, Obj? scope)
iterate
src virtual override Writer? iterate(Iteration? iteration, Writer? writer, Obj? object, List? scopes)
make
src new make(HxContext context)
MustacheDict
src const class MustacheDict : TypedDict
Constructors
| make |
Methods
| allTags | |
| markerTags | |
| valueTags | |
| wrap | |
| wrapAny | |
| wrapGrid | |
| wrapList |
Slot Details
allTags
src NameValuePair[] allTags()
make
src new make(Dict rec, |This| f)
markerTags
src Str[] markerTags()
valueTags
src NameValuePair[] valueTags()
wrap
src static MustacheDict wrap(Dict rec)
wrapAny
src static Obj wrapAny(Obj item)
wrapGrid
src static Grid wrapGrid(Grid grid)
wrapList
src static List wrapList(List list)
MustacheFuncs
src class MustacheFuncs : Obj
Methods
| templatize |
Slot Details
templatize
src @Axon static Str templatize(Obj scope, Str template)
MustacheLib
src const class MustacheLib : HxLib
Slot Details
NameValuePair
src const class NameValuePair : Obj
Constructors
| make |
Fields
| name | |
| value |
Methods
| fromDict | |
| fromMap | |
| type |
Slot Details
fromDict
src static NameValuePair[] fromDict(Dict dict)
fromMap
src static NameValuePair[] fromMap(Map map)
make
src new make(Str name, Obj? value)
name
src const Str name
type
src Str type()
value
src const Obj? value
TemplateProcessor
src class TemplateProcessor : Obj
Constructors
| make |
Methods
| applyTemplate | |
| parseTemplate |
Slot Details
applyTemplate
src Str applyTemplate(Obj item)
make
src new make(HxContext context)
parseTemplate
src Void parseTemplate(Str templateStr, [Str:Str]? partials := null)
using [java] com.github.mustachejava::MustacheResolver
using [java] java.io::StringReader
using [java] java.io::Reader
using hx
using hxIO
using haystack
class FinstackMustacheResolver : MustacheResolver
{
HxContext context
private Str:Str templates := [:]
new make(HxContext context)
{
this.context = context
}
public Void addTemplate(Str name, Str template)
{
this.templates.set(name.trim, template)
}
override public Reader? getReader(Str? resourceName)
{
Str template := ""
if (resourceName.startsWith("@"))
{
// Look up partial in the DB
templateRec := context.db.readById(Ref(resourceName), false)
if (templateRec != null)
{
templateHandle := IOHandle.fromObj(this.context.rt, templateRec)
template = templateHandle.inToBuf.readAllStr
}
}
else
template = this.templates.get(resourceName.trim) ?: ""
return StringReader(template)
}
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using [java] com.github.mustachejava.codes::DefaultMustache
using [java] com.github.mustachejava::MustacheFactory
using [java] com.github.mustachejava::TemplateContext
using [java] com.github.mustachejava::Code
using [java] com.github.mustachejava.util::InternalArrayList
using [java] java.util::List as JavaList
using [java] java.io::Writer
using haystack
class HaystackMustache : DefaultMustache
{
new make(
TemplateContext cx,
MustacheFactory factory,
Code[] codes,
Str name) : super(cx, factory, codes, name)
{
}
override public Writer? run(Writer? writer, JavaList? scopes)
{
return super.run(
writer,
this.mapScopesToHaystack(scopes)
)
}
private JavaList? mapScopesToHaystack(JavaList? scopes)
{
if (scopes == null)
return null
items := (Obj[]) scopes.toArray([,])
result := InternalArrayList()
items.each |item|
{
if (item is Grid)
{
result.add(((Grid) item).map |row|
{
MustacheDict.wrap(row)
})
}
else
if (item is Dict)
{
result.add(MustacheDict.wrap(item))
}
else
{
result.add(item)
}
}
return result
}
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using [java] com.github.mustachejava::DefaultMustacheFactory
using [java] com.github.mustachejava::MustacheVisitor
using hx
class HaystackMustacheFactory : DefaultMustacheFactory
{
private FinstackMustacheResolver resolver
new make(HxContext context, FinstackMustacheResolver resolver): super(resolver)
{
this.oh = HaystackObjectHandler(context)
this.resolver = resolver
}
override public MustacheVisitor? createMustacheVisitor()
{
return HaystackMustacheVisitor(this)
}
// We will enable passing partials in and caching them. The factory doesn't have access
// to the file system. The result will be looked up in the FinstackMustacheResovler
// It will allow names that are added or records. {{> @12345 }}
override public Str? resolvePartialPath(Str? dir, Str? name, Str? extension)
{
return name
}
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using [java] com.github.mustachejava::DefaultMustacheVisitor
using [java] com.github.mustachejava::Mustache
using [java] com.github.mustachejava::MustacheFactory
using [java] com.github.mustachejava::TemplateContext
using [java] com.github.mustachejava::Code
class HaystackMustacheVisitor : DefaultMustacheVisitor
{
private static const Code[] EMPTY_CODES := [,]
new make(MustacheFactory factory) : super(factory)
{
}
override public Mustache? mustache(TemplateContext? cx)
{
return HaystackMustache(
cx,
this.df,
this.list.toArray(EMPTY_CODES),
cx.file()
)
}
}
// resolvePartialPath gives path name
// factory.compilePartial(pathName)Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using [java] com.github.mustachejava.reflect::SimpleObjectHandler
using [java] com.github.mustachejava.reflect::AbstractObjectHandler
using [java] com.github.mustachejava.util::Wrapper
using [java] com.github.mustachejava.util::InternalArrayList
using [java] com.github.mustachejava::Iteration
using [java] java.util::List as JavaList
using [java] fanx.interop::Interop
using [java] java.io::Writer
using haystack
using hx
class HaystackObjectHandler : SimpleObjectHandler
{
private HxContext context
new make(HxContext context): super()
{
this.context = context
}
public override Obj? get(Str? name, Obj? scope)
{
try
{
if (scope is Dict || scope is Map)
return this.processDict(scope, name)
if (scope is Ref)
return this.processRef((Ref) scope, name)
/*
if (scope is Grid)
return this.processGrid((Grid) scope, name)
if (scope is List)
return this.processList((List) scope, name)
*/
if (scope is Str || scope is DateTime || scope is Time || scope is Number)
{
// TODO: create a better way to handle this.
// this only calls a method that takes no paramters
// properly parse and enable method calls with paramters
// in the template. (i.e. {{date.toLocal("mm-dd-yyyy")}} )
// May be possible to treat {{ expression }} as axon
// and evaluate it against the scope
// Possible method
result := this.callMethod(name, scope)
if (result != null)
return result
}
return super.get(name, scope)
}
catch (Err error)
{
echo ("Error occurred: $error")
}
return "?"
}
public override Wrapper? find(Str? name, JavaList? scopes)
{
if (name?.contains("->") == true)
{
return FantomWrapper() | JavaList? innerScopes -> Obj? |
{
for (i := innerScopes.size - 1; i >= 0; i--)
{
scope := innerScopes.get(i)
if (scope != null)
{
index := name.index("->")
if (index == null)
{
result := get(name, scope)
if (result != SimpleObjectHandler.NOT_FOUND)
return result
}
else
{
subScopes := makeInternalList(scope)
wrapper := find(name[0..index - 1], subScopes)
if (wrapper != null)
{
scope = wrapper.call(subScopes)
if (scope == null)
continue
// DeReference
subScopes = makeInternalList(
this.processRef(scope)
)
return find(
name[index + 2..-1],
subScopes
).call(subScopes)
}
}
}
}
return null
} as Wrapper
}
return super.find(name, scopes)
}
public override Writer? iterate(
Iteration? iteration,
Writer? writer,
Obj? object,
JavaList? scopes)
{
if (object is Grid)
object = ((Grid) object).toRows
if (object is List)
object = this.processList(object)
return super.iterate(iteration, writer, object, scopes)
}
private Obj? processGrid(Grid grid, Str? tagName := null)
{
if (tagName == "meta")
return grid.meta
if (tagName == "size")
return grid.size
return this.processList(grid.toRows)
}
private Obj? processList(List list, Str? tagName := null)
{
if (tagName == "size")
return list.size
return Interop.toJava(
MustacheDict.wrapList(list)
)
}
private Obj? processDict(Obj dict, Str? tagName)
{
if (tagName == null)
{
if (dict is Dict)
return MustacheDict.wrap(dict)
if (dict is Map)
return NameValuePair.fromMap(dict)
return dict
}
Obj? result
if (dict is Dict)
{
if (((Dict) dict).has(tagName))
{
result = ((Dict) dict).get(tagName)
if (result is Str && ((Str) result).contains("\$"))
result = Etc.macro(result, dict)
}
else
{
// Check for methods or fields of the object
result = this.callMethod(tagName, dict)
}
}
else
{
// Map
result = ((Map) dict).containsKey(tagName)
? ((Map) dict).get(tagName, false)
: null
}
if (result != null)
{
if (result is Grid)
return this.processGrid((Grid) result)
if (result is List)
return this.processList((List) result)
if (result is Dict)
result = MustacheDict.wrap(result)
return result
}
return AbstractObjectHandler.NOT_FOUND
}
private Obj? processRef(Ref ref, Str? name := null)
{
if (name == "dis")
return ref.dis
if (name == "id")
return ref.id
record := this.context.db.readById(ref, false)
if (record != null)
{
if (name != null)
{
if (record.has(name))
{
recTagVal := record.get(name)
if (recTagVal is Dict)
return MustacheDict.wrap(recTagVal)
return recTagVal
}
}
else
return MustacheDict.wrap(record)
}
return AbstractObjectHandler.NOT_FOUND
}
private Obj? callMethod(Str name, Obj scope)
{
// Check for methods or fields of the object
method := scope.typeof.method(name, false)
if (method != null)
{
return method.call(scope)
}
else
{
field := scope.typeof.field(name, false)
if (field != null)
return field.get(scope)
}
return null
}
private JavaList makeInternalList(Obj scope)
{
scopes := InternalArrayList()
scopes.add(scope)
return scopes
}
}
const class MustacheDict : TypedDict
{
new make(Dict rec, | This | f) : super(rec)
{
f(this)
}
public Str[] markerTags()
{
tags := [,]
this.each |value, tagName|
{
if (value is Marker)
tags.add(tagName)
}
return tags
}
public NameValuePair[] valueTags()
{
values := [,]
this.each |value, tagName|
{
if (value is Marker)
return // Skip Markers
values.add(NameValuePair(tagName, value))
}
return values
}
public NameValuePair[] allTags()
{
return NameValuePair.fromDict(this)
}
public static Obj wrapAny(Obj item)
{
if (item is Grid)
return wrapGrid(item)
if (item is List)
return wrapList(item)
if (item is Dict)
return wrap(item)
return item
}
public static MustacheDict wrap(Dict rec)
{
return create(MustacheDict#, rec)
}
public static Grid wrapGrid(Grid grid)
{
return grid.map |row| {
return MustacheDict.wrapAny(row)
}
}
public static List wrapList(List list)
{
return list.map | item | {
return MustacheDict.wrapAny(item)
}
}
}
class FantomWrapper : Wrapper
{
| JavaList? scopes -> Obj? | wrappedFunc
new make(| JavaList? scopes -> Obj? | f)
{
this.wrappedFunc = f
}
override public Obj? call(JavaList? scopes)
{
return this.wrappedFunc.call(scopes)
}
}
const class NameValuePair
{
const Str name
const Obj? value
new make(Str name, Obj? value)
{
this.name = name
this.value = value
}
public Str type()
{
if (this.value == null)
return "null"
return this.value.typeof.name
}
public static NameValuePair[] fromMap(Map map)
{
result := [,]
map.each |value, name| {
result.add(NameValuePair(name, value))
}
return result
}
public static NameValuePair[] fromDict(Dict dict)
{
result := [,]
dict.each |value, name| {
result.add(NameValuePair(name, value))
}
return result
}
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using haystack
using hx
using axon
class MustacheFuncs
{
// Mustache
@Axon
public static Str templatize(Obj scope, Str template)
{
processor := TemplateProcessor(HxContext.curHx)
processor.parseTemplate(template)
return processor.applyTemplate(scope)
}
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using hx
const class MustacheLib : HxLib
{
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST
using hx
using haystack
using [java] com.github.mustachejava::Mustache
using [java] com.github.mustachejava::DefaultMustacheFactory
using [java] java.io::StringReader
using [java] java.io::StringWriter
class TemplateProcessor
{
private DefaultMustacheFactory factory
private FinstackMustacheResolver resolver
private Mustache? template
new make(HxContext context)
{
this.resolver = FinstackMustacheResolver(context)
this.factory = HaystackMustacheFactory(
context,
this.resolver
)
}
public Void parseTemplate(Str templateStr, [Str:Str]? partials := null)
{
if (partials != null)
{
partials.each |partialStr, partialName| {
echo ("Adding Partial: $partialName")
this.resolver.addTemplate(partialName, partialStr)
}
}
this.template = this.factory.compile(StringReader(templateStr), "template")
}
public Str applyTemplate(Obj item)
{
writer := StringWriter(1000)
this.template.execute(
writer,
[item]
)
writer.flush
return writer.toString
}
}Skyspark 3.1.12 ∙ 20-Oct-2026 14:19 CEST