package com.lihaoyi.workbench import akka.actor.{ActorRef, Actor, ActorSystem} import com.typesafe.config.ConfigFactory import sbt.IO import spray.routing.SimpleRoutingApp import akka.actor.ActorDSL._ import scala.Some import upickle.{Writer, Json, Js} import spray.http.{AllOrigins, HttpResponse} import spray.http.HttpHeaders.`Access-Control-Allow-Origin` import concurrent.duration._ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingApp{ implicit val system = ActorSystem( "SystemLol", config = ConfigFactory.load(ActorSystem.getClass.getClassLoader), classLoader = ActorSystem.getClass.getClassLoader ) val pubSub = actor(new Actor{ var waitingActor: Option[ActorRef] = None var queuedMessages = List[Js.Value]() case object Clear import system.dispatcher system.scheduler.schedule(0 seconds, 10 seconds, self, Clear) def respond(a: ActorRef, s: String) = { a ! HttpResponse( entity = s, headers = List(`Access-Control-Allow-Origin`(AllOrigins)) ) } def receive = (x: Any) => (x, waitingActor, queuedMessages) match { case (a: ActorRef, _, Nil) => // Even if there's someone already waiting, // a new actor waiting replaces the old one waitingActor = Some(a) case (a: ActorRef, None, msgs) => respond(a, Json.write(Js.Array(msgs))) queuedMessages = Nil case (msg: Js.Array, None, msgs) => queuedMessages = msg :: msgs case (msg: Js.Array, Some(a), Nil) => respond(a, Json.write(Js.Array(Seq(msg)))) waitingActor = None case (Clear, Some(a), Nil) => respond(a, Json.write(Js.Array(Nil))) waitingActor = None } }) startServer(url, port) { get { path("workbench.js") { complete { IO.readStream( getClass.getClassLoader .getResourceAsStream("workbench_template.js") ).replace("", url) .replace("", port.toString) .replace("", bootSnippet) } } ~ getFromDirectory(".") } ~ post { path("notifications") { ctx => pubSub ! ctx.responder } } } def msg[T: Writer](t: T) = { pubSub ! upickle.writeJs(t) } def kill() = { system.shutdown() } }