diff --git a/src/main/scala/scaltex/BaseActor.scala b/src/main/scala/scaltex/BaseActor.scala index be93947..2a35318 100644 --- a/src/main/scala/scaltex/BaseActor.scala +++ b/src/main/scala/scaltex/BaseActor.scala @@ -4,7 +4,7 @@ import akka.actor.Actor import akka.actor.ActorRef import akka.actor.ActorSelection -import com.github.pathikrit.dijon.`{}` +import com.github.pathikrit.dijon.{ `{}`, `[]` } import com.github.pathikrit.dijon.parse import com.m3.curly.HTTP @@ -25,10 +25,12 @@ abstract class BaseActor(updater: ActorRef) extends Actor with DiscoverReference // State (to be saved in DB): var state = `{}` this.state._id = self.path.name + this.state.variableName = "" this.state.documentElement = "" this.state.contentSrc = "" this.state.contentRepr = "" this.state.contentEval = "" + this.state.contentUnified = `[]` var rev = "" var stateHash = 0 @@ -42,6 +44,9 @@ abstract class BaseActor(updater: ActorRef) extends Actor with DiscoverReference case Change(to) => `change current doc elem`(to) + case ChangeName(to) => + `change variable name`(to) + case Next(id) => `change next ref`(id) @@ -62,8 +67,8 @@ abstract class BaseActor(updater: ActorRef) extends Actor with DiscoverReference case RequestForCodeGen(requester, others) => `reply with code, pass request along`(requester, others) - case ReplyForCodeGen(code, replyEnd) => - `buffer code then trigger interpretation`(code, replyEnd) + case ReplyForCodeGen(code, shortName, replyEnd) => + `buffer code then trigger interpretation`(code, shortName, replyEnd) case ReturnValue(repr) => `change content repr, send curr state`(repr) @@ -202,14 +207,53 @@ abstract class BaseActor(updater: ActorRef) extends Actor with DiscoverReference if (!triggered) sendCurrentState } - def `buffer code then trigger interpretation`(code: String, replyEnd: Boolean): Unit = { + def `buffer code then trigger interpretation`(code: String, shortName: Tuple2[String, String], replyEnd: Boolean): Unit = { replyCodeBuffer += code + val uuid = shortName._1 + val name = shortName._2 + replyNameBuffer(uuid) = name if (replyEnd) triggerInterpreter } def `change content repr, send curr state`(repr: Any): Unit = { - this.state.contentRepr = repr.toString + val reprTuple = repr.asInstanceOf[scaltex.utils.StringContext.Unify] + this.state.contentRepr = reprTuple._1.toString documentElement._gotUpdate(this.state, refs) + // TODO: refactor + val results = reprTuple._2 + val staticParts = reprTuple._3 + + val contentSrc = this.state.contentSrc.as[String].get + val expressions = expRegex.findAllIn(contentSrc).map(_.toString).toList + val splittedExpr = expressions.map(_.split(uuidRegex.toString)).toList + val uuids = expressions.map(uuidRegex.findAllMatchIn(_).map(_.toString).toList) + + val shortNames = replyNameBuffer.toMap + replyNameBuffer.clear // TODO: risky? Other interpreation may already run? + + // equal size: expressions, splittedExpr, uuids->shortName, results + // size + 1: static parts + for (idx <- 0 until results.size) { + this.state.contentUnified(idx) = `{}` + this.state.contentUnified(idx).str = staticParts(idx) + this.state.contentUnified(idx).result = results(idx) + this.state.contentUnified(idx).expression = `[]` + var currJsonIdx = 0 + for (jdx <- 0 until splittedExpr(idx).size) { + this.state.contentUnified(idx).expression(currJsonIdx) = splittedExpr(idx)(jdx) + if (uuids(idx).indices.contains(jdx)) { + currJsonIdx += 1 + this.state.contentUnified(idx).expression(currJsonIdx) = `{}` + this.state.contentUnified(idx).expression(currJsonIdx).uuid = uuids(idx)(jdx) + this.state.contentUnified(idx).expression(currJsonIdx).shortName = shortNames(uuids(idx)(jdx)) + } + currJsonIdx += 1 + } + } + + this.state.contentUnified(staticParts.size - 1) = `{}` + this.state.contentUnified(staticParts.size - 1).str = staticParts.last + sendCurrentState } @@ -225,4 +269,8 @@ abstract class BaseActor(updater: ActorRef) extends Actor with DiscoverReference this.state.documentElement = to } + def `change variable name`(to: String): Unit = { + this.state.variableName = to + } + } diff --git a/src/main/scala/scaltex/DiscoverReferences.scala b/src/main/scala/scaltex/DiscoverReferences.scala index 345e31c..74f4409 100644 --- a/src/main/scala/scaltex/DiscoverReferences.scala +++ b/src/main/scala/scaltex/DiscoverReferences.scala @@ -2,12 +2,17 @@ package scaltex import Messages._ import akka.actor.ActorRef +import scala.collection.mutable.Buffer +import scala.collection.mutable.Map trait DiscoverReferences { this: BaseActor => + val uuidRegex = "id_[\\$_a-zA-Z0-9]*_id".r + val expRegex = """\$\{.*?\}""".r + def findAllActorRefs(in: String) = { - val allIds = "id_[\\$_a-zA-Z0-9]*_id".r.findAllIn(in) + val allIds = uuidRegex.findAllIn(in) val withCuttedIds = allIds.map(x => x.slice(3, x.size - 3)) withCuttedIds.toList } @@ -24,7 +29,9 @@ trait DiscoverReferences { } def `reply with code, pass request along`(requester: ActorRef, others: List[String]): Unit = { - requester ! ReplyForCodeGen(genCode, others.size == 0) + val shortName = this.state.variableName.as[String].get + val uuid = "id_" + this.id + "_id" + requester ! ReplyForCodeGen(genCode, (uuid, shortName), others.size == 0) if (others.size > 0) { val codeGenRequest = RequestForCodeGen(requester, others.tail) root ! Pass(others.head, codeGenRequest) @@ -40,19 +47,21 @@ trait DiscoverReferences { code } - val replyCodeBuffer = scala.collection.mutable.Buffer[String]() + val replyCodeBuffer = Buffer[String]() + val replyNameBuffer = Map[String, String]() def triggerInterpreter = { val references = replyCodeBuffer.toSet.mkString("\n") replyCodeBuffer.clear // Note: this.state.contentSrc delivers already quotes -> "..." - val content = "s\"\"" + this.state.contentSrc + "\"\"" + val content = "unify\"\"" + this.state.contentSrc + "\"\"" val completeCode = s""" | import com.github.pathikrit.dijon.JsonStringContext + | import scaltex.utils.StringContext.Unifier | ${references} - | val contentRepr = ${content} - | contentRepr + | val (contentRepr, exprResults, staticParts) = ${content} + | (contentRepr, exprResults, staticParts) """.stripMargin val interpreterActor = context.actorSelection("/user/interpreter") // TODO: inject from outside? diff --git a/src/main/scala/scaltex/Messages.scala b/src/main/scala/scaltex/Messages.scala index 16f3444..fc416fd 100644 --- a/src/main/scala/scaltex/Messages.scala +++ b/src/main/scala/scaltex/Messages.scala @@ -13,6 +13,7 @@ object Messages { // modify actors case class Change(to: String) + case class ChangeName(to: String) case class Content(content: String) case class UpdateStateProperty(json: String) @@ -23,7 +24,7 @@ object Messages { case class Interpret(code: String, returnId: String) case class ReturnValue(is: Any) case class RequestForCodeGen(requester: ActorRef, others: List[String]) - case class ReplyForCodeGen(code: String, replyEnd: Boolean) + case class ReplyForCodeGen(code: String, shortName: Tuple2[String, String], replyEnd: Boolean) // Messages for registring websockets case class RegisterWebsocket(ref: ActorRef) diff --git a/src/main/scala/scaltex/utils/UnifierStringContext.scala b/src/main/scala/scaltex/utils/UnifierStringContext.scala new file mode 100644 index 0000000..26e605b --- /dev/null +++ b/src/main/scala/scaltex/utils/UnifierStringContext.scala @@ -0,0 +1,15 @@ +package scaltex.utils + +object StringContext { + + type Unify = Tuple3[String, List[String], List[String]] + + implicit class Unifier(val sc: StringContext) { + def unify(args: Any*): Unify = { + val exprResults = args.map(_.toString).toList + val parts = sc.parts.toList + (sc.s(args: _*), exprResults, parts) + } + } + +} \ No newline at end of file diff --git a/src/test/scala/scaltex/DiscoverReferencesSpec.scala b/src/test/scala/scaltex/DiscoverReferencesSpec.scala index 2f1b0df..f756282 100644 --- a/src/test/scala/scaltex/DiscoverReferencesSpec.scala +++ b/src/test/scala/scaltex/DiscoverReferencesSpec.scala @@ -112,4 +112,25 @@ class DiscoverReferencesSpec } + "Unified content" should { + + "be an array with the expression details" in { + `1` ! ChangeName("nameOfX") + `2` ! Update + + updater.expectMsgPF() { + case CurrentState(json) => + val state = parse(json) + state._id should be(`2`.path.name) + state.contentRepr should be("Has one reference: Section!") + state.contentUnified(0).str should be ("Has one reference: ") + state.contentUnified(0).result should be ("Section") + state.contentUnified(0).expression(0) should be ("${") + state.contentUnified(0).expression(1).uuid should be ("id_" + `1`.path.name + "_id") + state.contentUnified(0).expression(1).shortName should be ("nameOfX") + state.contentUnified(0).expression(2) should be (".getClass.getSimpleName}") + } + } + } + } \ No newline at end of file diff --git a/src/test/scala/scaltex/models/ReportSpec.scala b/src/test/scala/scaltex/models/ReportSpec.scala index 0624940..e5aa0f5 100644 --- a/src/test/scala/scaltex/models/ReportSpec.scala +++ b/src/test/scala/scaltex/models/ReportSpec.scala @@ -195,4 +195,22 @@ class ReportSpec } + "A element with a variable (short) name" should { + + "be part of the base actor state" in { + val ref = TestActorRef(new AvailableModels.Report(updater.ref)) + + ref.underlyingActor.state.variableName should be("") + } + + "be changeable" in { + val ref = TestActorRef(new AvailableModels.Report(updater.ref)) + + ref.underlyingActor.state.variableName should be("") + ref ! ChangeName("myName") + ref.underlyingActor.state.variableName should be("myName") + } + + } + }