From 7c3bf500f086bd741703f3c5c87d7a74dd01703c Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sun, 10 Aug 2014 16:40:20 -0700 Subject: Moved all the server stuff into a separate file distinct from all the SBT madness --- Plugin.scala | 86 ++++++---------------- Server.scala | 77 +++++++++++++++++++ .../src/main/scala/example/ScalaJSExample.scala | 2 +- 3 files changed, 99 insertions(+), 66 deletions(-) create mode 100644 Server.scala diff --git a/Plugin.scala b/Plugin.scala index dab630e..512e195 100644 --- a/Plugin.scala +++ b/Plugin.scala @@ -13,52 +13,20 @@ import spray.http.{AllOrigins, HttpResponse} import spray.routing.SimpleRoutingApp import spray.http.HttpHeaders.`Access-Control-Allow-Origin` -object Plugin extends sbt.Plugin with SimpleRoutingApp{ - implicit val system = ActorSystem( - "SystemLol", - config = ConfigFactory.load(ActorSystem.getClass.getClassLoader), - classLoader = ActorSystem.getClass.getClassLoader - ) +object Plugin extends sbt.Plugin { + + val refreshBrowsers = taskKey[Unit]("Sends a message to all connected web pages asking them to refresh the page") val updateBrowsers = taskKey[Unit]("partially resets some of the stuff in the browser") val localUrl = settingKey[(String, Int)]("localUrl") - private[this] val routes = settingKey[Unit]("local websocket server") + private[this] val server = settingKey[Server]("local websocket server") + + val bootSnippet = settingKey[String]("piece of javascript to make things happen") val updatedJS = taskKey[List[String]]("Provides the addresses of the JS files that have changed") - 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 - } - }) val workbenchSettings = Seq( localUrl := ("localhost", 12345), @@ -85,9 +53,9 @@ object Plugin extends sbt.Plugin with SimpleRoutingApp{ val clientLogger = FullLogger{ new Logger { def log(level: Level.Value, message: => String) = - if(level >= Level.Info) pubSub ! upickle.writeJs(Seq("print", level.toString(), message)) - def success(message: => String) = pubSub ! upickle.writeJs(Seq("print", "info", message)) - def trace(t: => Throwable) = pubSub ! upickle.writeJs(Seq("print", "error", t.toString)) + if(level >= Level.Info) server.value msg Seq("print", level.toString, message) + def success(message: => String) = server.value msg Seq("print", "info", message) + def trace(t: => Throwable) = server.value msg Seq("print", "error", t.toString) } } clientLogger.setSuccessEnabled(true) @@ -96,45 +64,33 @@ object Plugin extends sbt.Plugin with SimpleRoutingApp{ }, refreshBrowsers := { streams.value.log.info("workbench: Reloading Pages...") - pubSub ! upickle.writeJs(Seq("reload")) + server.value msg Seq("reload") }, updateBrowsers := { val changed = updatedJS.value // There is no point in clearing the browser if no js files have changed. if (changed.length > 0) { - pubSub ! upickle.writeJs(Seq("clear")) + server.value msg Seq("clear") changed.foreach { path => streams.value.log.info("workbench: Refreshing " + path) - pubSub ! upickle.writeJs(Seq( + server.value msg Seq( "run", path, bootSnippet.value - )) + ) } } }, - routes := startServer(localUrl.value._1, localUrl.value._2){ - get{ - path("workbench.js"){ - complete{ - IO.readStream( - getClass.getClassLoader - .getResourceAsStream("workbench_template.js") - ).replace("", localUrl.value._1) - .replace("", localUrl.value._2.toString) - .replace("", bootSnippet.value) - } - } ~ - getFromDirectory(".") - } ~ - post{ - path("notifications"){ ctx => - pubSub ! ctx.responder - } - } - + server := { + new Server(localUrl.value._1, localUrl.value._2, bootSnippet.value) + }, + onLoad := { state => + state + }, + onUnload := { state => + state } ) } diff --git a/Server.scala b/Server.scala new file mode 100644 index 0000000..af4b6b6 --- /dev/null +++ b/Server.scala @@ -0,0 +1,77 @@ +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) + } +} \ No newline at end of file diff --git a/example/src/main/scala/example/ScalaJSExample.scala b/example/src/main/scala/example/ScalaJSExample.scala index 7222143..8509a0e 100644 --- a/example/src/main/scala/example/ScalaJSExample.scala +++ b/example/src/main/scala/example/ScalaJSExample.scala @@ -21,7 +21,7 @@ object ScalaJSExample { val corners = Seq(Point(255, 255), Point(0, 255), Point(128, 0)) def clear() = { - ctx.fillStyle = "black" + ctx.fillStyle = "white" ctx.fillRect(0, 0, 255, 255) } -- cgit v1.2.3