using afPlastic
** Contains contextual information about a Fancordion command.
const class CommandCtx {
private static const PlasticCompiler compiler := PlasticCompiler()
** The command URI.
const Str cmdUri
** The *scheme* portion of the command URI:
**
** [text]`scheme:path`
const Str cmdScheme
** The *path* portion of the command URI (minus the scheme):
**
** [text]`scheme:path`
const Str cmdPath
** The *text* portion of the command:
**
** [text]`scheme:path`
**
** For table column commands this is the column text.
const Str cmdText
** The 0-based table row index. Only available in table row commands.
const Int? tableRowIdx
** The columns that make up a table row. Only available in table row commands.
const Str[]? tableCols
** Is set to 'true' if there has been previous errors in the fixture and this command should be ignored.
const Bool ignore
internal new make(Str cmdScheme, Str cmdPath, Str cmdText, Int? tableRow, Str[]? tableCols, Bool ignore) {
this.cmdUri = "${cmdScheme}:${cmdPath}"
this.cmdScheme = cmdScheme
this.cmdPath = cmdPath
this.cmdText = cmdText
this.tableRowIdx= tableRow
this.tableCols = tableCols
this.ignore = ignore
}
** Applies Fancordion variables to the given str.
** Specifically it replaces portions of the string with:
**
** - '#TEXT -> cmdText.toCode'
** - '#ROW -> tableRowIdx'
** - '#COLS -> tableCols.toCode'
** - '#COL[0] -> tableCols[0].toCode'
** - '#COL[1] -> tableCols[1].toCode'
** - '#COL[n] -> tableCols[n].toCode'
** - '#FIXTURE -> "fixture"'
Str applyVariables(Str text := cmdPath) {
text = text.replace("#TEXT", cmdText.toCode)
if (tableRowIdx != null)
text = text.replace("#ROW", tableRowIdx.toCode)
tableCols?.each |col, i| {
text = text.replace("#COL[${i}]", tableCols[i].toCode)
}
if (tableCols != null)
text = text.replace("#COLS", tableCols.toCode)
text = text.replace("#FIXTURE", "fixture")
return text
}
** Executes the given code against the fixture instance. Example:
**
** executeOnFixture(fixture, "echo()") --> fixture.echo()
Void executeOnFixture(Obj fixture, Str code) {
model := PlasticClassModel("FixtureExecutor", false).extend(FixtureExecutor#)
body := isSlotty(fixture, code)
? "fixture := (${fixture.typeof.qname}) obj;\nfixture.${code}"
: "fixture := (${fixture.typeof.qname}) obj;\n${code}"
model.overrideMethod(FixtureExecutor#executeOn, body)
if (fixture.typeof.pod != null)
model.usingPod(fixture.typeof.pod)
help := (FixtureExecutor) compiler.compileModel(model).make
help.executeOn(fixture)
}
** Executes the given code on the fixture instance and returns a value. Example:
**
** getFromFixture(fixture, "toStr()") --> return fixture.toStr()
Obj? getFromFixture(Obj fixture, Str code) {
model := PlasticClassModel("FixtureExecutor", false).extend(FixtureExecutor#)
body := isSlotty(fixture, code)
? "fixture := (${fixture.typeof.qname}) obj;\nreturn fixture.${code}"
: "fixture := (${fixture.typeof.qname}) obj;\nreturn ${code}"
model.overrideMethod(FixtureExecutor#getFrom, body)
if (fixture.typeof.pod != null)
model.usingPod(fixture.typeof.pod)
help := (FixtureExecutor) compiler.compileModel(model).make
return help.getFrom(fixture)
}
internal static Bool isSlotty(Obj fixture, Str code) {
slotName := ""
code.chars.eachWhile |char->Bool?| {
if (char.isAlphaNum || char == ':') {
slotName += char.toChar
return null
}
return true
}
if (slotName.contains("::"))
return false
return fixture.typeof.slot(slotName, false) != null
}
}
@NoDoc
abstract class FixtureExecutor {
virtual Void executeOn(Obj obj) { }
virtual Obj? getFrom (Obj obj) { null }
}