Skip to content

Commit f4e3ab3

Browse files
committed
chicken
1 parent 013a9c6 commit f4e3ab3

File tree

15 files changed

+533
-0
lines changed

15 files changed

+533
-0
lines changed

build.sbt

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import sbt.Keys._
2+
import sbt.Project.projectToRef
3+
4+
// a special crossProject for configuring a JS/JVM/shared structure
5+
lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared"))
6+
.settings(
7+
scalaVersion := Settings.versions.scala,
8+
libraryDependencies ++= Settings.sharedDependencies.value
9+
)
10+
// set up settings specific to the JS project
11+
.jsConfigure(_ enablePlugins ScalaJSPlay)
12+
13+
lazy val sharedJVM = shared.jvm.settings(name := "sharedJVM")
14+
15+
lazy val sharedJS = shared.js.settings(name := "sharedJS")
16+
17+
// use eliding to drop some debug code in the production build
18+
lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable functions")
19+
20+
// instantiate the JS project for SBT with some additional settings
21+
lazy val client: Project = (project in file("client"))
22+
.settings(
23+
name := "client",
24+
version := Settings.version,
25+
scalaVersion := Settings.versions.scala,
26+
scalacOptions ++= Settings.scalacOptions,
27+
libraryDependencies ++= Settings.scalajsDependencies.value,
28+
// by default we do development build, no eliding
29+
elideOptions := Seq(),
30+
scalacOptions ++= elideOptions.value,
31+
jsDependencies ++= Settings.jsDependencies.value,
32+
// RuntimeDOM is needed for tests
33+
jsDependencies += RuntimeDOM % "test",
34+
// yes, we want to package JS dependencies
35+
skip in packageJSDependencies := false,
36+
// use Scala.js provided launcher code to start the client app
37+
persistLauncher := true,
38+
persistLauncher in Test := false,
39+
// use uTest framework for tests
40+
testFrameworks += new TestFramework("utest.runner.Framework")
41+
)
42+
.enablePlugins(ScalaJSPlugin, ScalaJSPlay)
43+
.dependsOn(sharedJS)
44+
45+
// Client projects (just one in this case)
46+
lazy val clients = Seq(client)
47+
48+
// instantiate the JVM project for SBT with some additional settings
49+
lazy val server = (project in file("server"))
50+
.settings(
51+
name := "server",
52+
version := Settings.version,
53+
scalaVersion := Settings.versions.scala,
54+
scalacOptions ++= Settings.scalacOptions,
55+
libraryDependencies ++= Settings.jvmDependencies.value,
56+
commands += ReleaseCmd,
57+
// connect to the client project
58+
scalaJSProjects := clients,
59+
pipelineStages := Seq(scalaJSProd),
60+
// compress CSS
61+
LessKeys.compress in Assets := true
62+
)
63+
.enablePlugins(PlayScala)
64+
.disablePlugins(PlayLayoutPlugin) // use the standard directory layout instead of Play's custom
65+
.aggregate(clients.map(projectToRef): _*)
66+
.dependsOn(sharedJVM)
67+
68+
// Command for building a release
69+
lazy val ReleaseCmd = Command.command("release") {
70+
state => "set elideOptions in client := Seq(\"-Xelide-below\", \"WARNING\")" ::
71+
"client/clean" ::
72+
"client/test" ::
73+
"server/clean" ::
74+
"server/test" ::
75+
"server/dist" ::
76+
"set elideOptions in client := Seq()" ::
77+
state
78+
}
79+
80+
// lazy val root = (project in file(".")).aggregate(client, server)
81+
82+
// loads the Play server project at sbt startup
83+
onLoad in Global := (Command.process("project server", _: State)) compose (onLoad in Global).value
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package myob
2+
3+
//import myob.modules._
4+
//import myob.model._
5+
//import spatutorial.client.components.GlobalStyles
6+
//import spatutorial.client.modules._
7+
//import spatutorial.client.services.{RootModel, SPACircuit, ToLearns, Todos}
8+
9+
import japgolly.scalajs.react.{ReactDOM, ReactComponentB}
10+
import org.scalajs.dom
11+
12+
import scala.scalajs.js.JSApp
13+
import scala.scalajs.js.annotation.JSExport
14+
import japgolly.scalajs.react.vdom.prefix_<^._
15+
16+
17+
@JSExport("BankingRulesMain")
18+
object TutorialApp extends JSApp {
19+
def main(): Unit = {
20+
// appendPar(dom.document.body, "Hello World")
21+
val HelloMessage = ReactComponentB[String]("HelloMessage")
22+
.render($ => <.div("Hello ", $.props))
23+
.build
24+
25+
ReactDOM.render(HelloMessage("John"), dom.document.body)
26+
}
27+
def appendPar(targetNode: dom.Node, text: String): Unit = {
28+
val parNode = dom.document.createElement("p")
29+
val textNode = dom.document.createTextNode(text)
30+
parNode.appendChild(textNode)
31+
targetNode.appendChild(parNode)
32+
}
33+
}
34+
//object BankingRulesMain extends js.JSApp {
35+
//
36+
// // Define the locations (pages) used in this application
37+
// sealed trait Loc
38+
//
39+
// case object ListRulesLoc extends Loc
40+
// case class EditRuleLoc(ruleId:String) extends Loc
41+
//
42+
//// case object TodoLoc extends Loc
43+
//
44+
// val itemPage = ReactComponentB[EditRuleLoc]("Edit Rule Item page")
45+
// .render(p => <.div(s"Info page for Rule #${p.props.ruleId}"))
46+
//// OR -> .render_P((p) => <.div(s"Info page for Rule #${p.ruleId}"))
47+
// .build
48+
//
49+
//
50+
//
51+
//// dynamicRouteCT("item" / int.caseClass[ItemPage])
52+
//// ~> dynRender(itemPage(_))
53+
//
54+
// // configure the router
55+
// val routerConfig = RouterConfigDsl[Loc].buildConfig { (dsl: RouterConfigDsl[Loc]) =>
56+
// import dsl._
57+
//
58+
// // wrap/connect components to the circuit
59+
//// (staticRoute(root, DashboardLoc) ~> renderR(ctl => SPACircuit.wrap(_.motd)((proxy: ModelProxy[Pot[String]]) => Dashboard(ctl, proxy)))
60+
//// | staticRoute("#todo", TodoLoc) ~> renderR(ctl => SPACircuit.connect((model: RootModel) => model.todos)((proxy: ModelProxy[Pot[Todos]]) => Todo(proxy)))
61+
//// | staticRoute("#tolearn", ToLearnLoc) ~> renderR(ctl => SPACircuit.connect((model: RootModel) => model.toLearns)((proxy: ModelProxy[Pot[ToLearns]]) => ToLearn(proxy)))
62+
//// ).notFound(redirectToPage(DashboardLoc)(Redirect.Replace))
63+
//
64+
//// val r: Route[EditRuleLoc] = ("category" / string("[a-z]+") / uuid).caseClass[EditRuleLoc]
65+
//
66+
// (staticRoute(root, ListRulesLoc) ~> renderR(ctl => BankingRulesCircuit.wrap((rootModel: model.RootModel) => rootModel.rules)((proxy: ModelProxy[Pot[model.Rules]]) => modules.Rule(ctl, proxy)))
67+
// | dynamicRouteCT("#item" / string("rule-.*").caseClassDebug[EditRuleLoc]) ~> dynRender(props => itemPage(props))
68+
//
69+
// // | dynamicRouteCT("item" / int.caseClass[EditRuleLoc]) ~> renderR(ctl => BankingRulesCircuit.connect((model: RootModel) => model.todos)((proxy: ModelProxy[Pot[Todos]]) => Todo(proxy)))
70+
//// | staticRoute("#edit", EditRuleLoc) ~> renderR(ctl => BankingRulesCircuit.connect((model: RootModel) => model.todos)((proxy: ModelProxy[Pot[Todos]]) => Todo(proxy)))
71+
// ).notFound(redirectToPage(ListRulesLoc)(Redirect.Replace))
72+
// }.renderWith(layout)
73+
//
74+
// // base layout for all pages
75+
// def layout(c: RouterCtl[Loc], r: Resolution[Loc]) = {
76+
// <.div(
77+
// // here we use plain Bootstrap class names as these are specific to the top level layout defined here
78+
// <.nav(^.className := "navbar navbar-inverse navbar-fixed-top",
79+
// <.div(^.className := "container",
80+
// <.div(^.className := "navbar-header", <.span(^.className := "navbar-brand", "Banking Rules...")),
81+
// <.div(^.className := "collapse navbar-collapse",
82+
// // connect menu to model, because it needs to update when the number of open todos changes
83+
// BankingRulesCircuit.connect { value =>
84+
// val rules = value.rules.map(_.items.count(!_.completed)).toOption
85+
// rules
86+
// }((modelProxy: ModelProxy[Option[Int]]) => modules.MainMenu(c, r.page, modelProxy))
87+
//
88+
// )
89+
// )
90+
// ),
91+
// // currently active module is shown in this container
92+
// <.div(^.className := "container", r.render())
93+
// )
94+
// }
95+
//
96+
// @JSExport
97+
// def main(): Unit = {
98+
// log.warn("Application starting")
99+
// // send log messages also to the server
100+
// log.enableServerLogging("/logging")
101+
// log.info("This message goes to server as well")
102+
//
103+
// // create stylesheet
104+
// GlobalStyles.addToDocument()
105+
// // create the router
106+
// val router = Router(BaseUrl.until_#, routerConfig)
107+
// // tell React to render the router in the document body
108+
// ReactDOM.render(router(), dom.document.getElementById("root"))
109+
// }
110+
//}

index.html

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>The Scala.js Tutorial</title>
6+
</head>
7+
<body>
8+
<!-- Include Scala.js compiled code -->
9+
<script type="text/javascript" src="./client/target/scala-2.11/client-fastopt.js"></script>
10+
<!-- Run tutorial.webapp.TutorialApp -->
11+
<script type="text/javascript">
12+
myob.TutorialApp().main();
13+
</script>
14+
</body>
15+
</html>

project/Settings.scala

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import sbt._
2+
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
3+
4+
/**
5+
* Application settings. Configure the build for your application here.
6+
* You normally don't have to touch the actual build definition after this.
7+
*/
8+
object Settings {
9+
/** The name of your application */
10+
val name = "scalajs-spa"
11+
12+
/** The version of your application */
13+
val version = "1.1.0"
14+
15+
/** Options for the scala compiler */
16+
val scalacOptions = Seq(
17+
"-Xlint",
18+
"-unchecked",
19+
"-deprecation",
20+
"-feature"
21+
)
22+
23+
/** Declare global dependency versions here to avoid mismatches in multi part dependencies */
24+
object versions {
25+
val scala = "2.11.7"
26+
val scalaDom = "0.8.2"
27+
val scalajsReact = "0.10.2"
28+
val scalaCSS = "0.3.1"
29+
val log4js = "1.4.10"
30+
val autowire = "0.2.5"
31+
val booPickle = "1.1.0"
32+
val diode = "0.1.0"
33+
val uTest = "0.3.1"
34+
35+
val react = "0.14.3"
36+
val jQuery = "1.11.1"
37+
val bootstrap = "3.3.2"
38+
val chartjs = "1.0.1"
39+
40+
val playScripts = "0.3.0"
41+
}
42+
43+
/**
44+
* These dependencies are shared between JS and JVM projects
45+
* the special %%% function selects the correct version for each project
46+
*/
47+
val sharedDependencies = Def.setting(Seq(
48+
"com.lihaoyi" %%% "autowire" % versions.autowire,
49+
"me.chrons" %%% "boopickle" % versions.booPickle,
50+
"com.lihaoyi" %%% "utest" % versions.uTest
51+
))
52+
53+
/** Dependencies only used by the JVM project */
54+
val jvmDependencies = Def.setting(Seq(
55+
"com.vmunier" %% "play-scalajs-scripts" % versions.playScripts,
56+
"org.webjars" % "font-awesome" % "4.3.0-1" % Provided,
57+
"org.webjars" % "bootstrap" % versions.bootstrap % Provided
58+
))
59+
60+
/** Dependencies only used by the JS project (note the use of %%% instead of %%) */
61+
val scalajsDependencies = Def.setting(Seq(
62+
"com.github.japgolly.scalajs-react" %%% "core" % versions.scalajsReact,
63+
"com.github.japgolly.scalajs-react" %%% "extra" % versions.scalajsReact,
64+
"com.github.japgolly.scalacss" %%% "ext-react" % versions.scalaCSS,
65+
"me.chrons" %%% "diode" % versions.diode,
66+
"me.chrons" %%% "diode-react" % versions.diode,
67+
"org.scala-js" %%% "scalajs-dom" % versions.scalaDom
68+
))
69+
70+
/** Dependencies for external JS libs that are bundled into a single .js file according to dependency order */
71+
val jsDependencies = Def.setting(Seq(
72+
"org.webjars.bower" % "react" % versions.react / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React",
73+
"org.webjars.bower" % "react" % versions.react / "react-dom.js" minified "react-dom.min.js" dependsOn "react-with-addons.js" commonJSName "ReactDOM",
74+
"org.webjars" % "jquery" % versions.jQuery / "jquery.js" minified "jquery.min.js",
75+
"org.webjars" % "bootstrap" % versions.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "jquery.js",
76+
"org.webjars" % "chartjs" % versions.chartjs / "Chart.js" minified "Chart.min.js",
77+
"org.webjars" % "log4javascript" % versions.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js"
78+
))
79+
}

project/build.properties

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=0.13.8

project/plugins.sbt

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// repository for Typesafe plugins
2+
resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/"
3+
4+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5")
5+
6+
addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6")
7+
8+
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0")
9+
10+
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.3")
11+
12+
addSbtPlugin("com.vmunier" % "sbt-play-scalajs" % "0.2.8")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@import "lib/bootstrap/less/bootstrap.less";
2+
@import "lib/font-awesome/less/font-awesome.less";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
spatutorial {
2+
}

server/src/main/resources/routes

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Routes
2+
# This file defines all application routes (Higher priority routes first)
3+
# ~~~~
4+
5+
# Home page
6+
GET / controllers.Application.index
7+
8+
# Map static resources from the /public folder to the /assets URL path
9+
GET /assets/fonts/*file controllers.Assets.at(path="/public/lib/font-awesome/fonts", file)
10+
GET /assets/*file controllers.Assets.at(path="/public", file)
11+
12+
# Autowire calls
13+
POST /api/*path controllers.Application.autowireApi(path: String)
14+
15+
# Logging
16+
POST /logging controllers.Application.logging
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package controllers
2+
3+
import java.nio.ByteBuffer
4+
5+
import boopickle.Default._
6+
import play.api.mvc._
7+
import services.ApiService
8+
import spatutorial.shared.Api
9+
10+
import scala.concurrent.ExecutionContext.Implicits.global
11+
12+
object Router extends autowire.Server[ByteBuffer, Pickler, Pickler] {
13+
override def read[R: Pickler](p: ByteBuffer) = Unpickle[R].fromBytes(p)
14+
override def write[R: Pickler](r: R) = Pickle.intoBytes(r)
15+
}
16+
17+
object Application extends Controller {
18+
val apiService = new ApiService()
19+
20+
def index = Action {
21+
Ok(views.html.index("SPA tutorial"))
22+
}
23+
24+
def autowireApi(path: String) = Action.async(parse.raw) {
25+
implicit request =>
26+
println(s"Request path: $path")
27+
28+
// get the request body as Array[Byte]
29+
val b = request.body.asBytes(parse.UNLIMITED).get
30+
31+
// call Autowire route
32+
Router.route[Api](apiService)(
33+
autowire.Core.Request(path.split("/"), Unpickle[Map[String, ByteBuffer]].fromBytes(ByteBuffer.wrap(b)))
34+
).map(buffer => {
35+
val data = Array.ofDim[Byte](buffer.remaining())
36+
buffer.get(data)
37+
Ok(data)
38+
})
39+
}
40+
41+
def logging = Action(parse.anyContent) {
42+
implicit request =>
43+
request.body.asJson.foreach { msg =>
44+
println(s"CLIENT - $msg")
45+
}
46+
Ok("")
47+
}
48+
}

0 commit comments

Comments
 (0)